mirror of
https://github.com/penpot/penpot.git
synced 2025-05-24 13:36:11 +02:00
Merge remote-tracking branch 'origin/staging' into develop
This commit is contained in:
commit
c9ddc83eef
39 changed files with 678 additions and 450 deletions
17
CHANGES.md
17
CHANGES.md
|
@ -22,11 +22,11 @@
|
||||||
- Constraints are not well assigned when default and multiselection [Taiga #3069](https://tree.taiga.io/project/penpot/issue/3069)
|
- Constraints are not well assigned when default and multiselection [Taiga #3069](https://tree.taiga.io/project/penpot/issue/3069)
|
||||||
- Exporting big files flow [Taiga #2218](https://tree.taiga.io/project/penpot/us/2218)
|
- Exporting big files flow [Taiga #2218](https://tree.taiga.io/project/penpot/us/2218)
|
||||||
- Multiexport from main menu [Taiga #520](https://tree.taiga.io/project/penpot/us/28541)
|
- Multiexport from main menu [Taiga #520](https://tree.taiga.io/project/penpot/us/28541)
|
||||||
- Multipexport assets (aka bulk export) [Taiga #520](https://tree.taiga.io/project/penpot/us/520)
|
- Multiexport assets (aka bulk export) [Taiga #520](https://tree.taiga.io/project/penpot/us/520)
|
||||||
- Set the artboard layer fixed at the top side of the layers [Taiga #2636](https://tree.taiga.io/project/penpot/us/2636)
|
- Set the artboard layer fixed at the top side of the layers [Taiga #2636](https://tree.taiga.io/project/penpot/us/2636)
|
||||||
- Set an artboard as the file thumbnail [Taiga #1526](https://tree.taiga.io/project/penpot/us/1526)
|
- Set an artboard as the file thumbnail [Taiga #1526](https://tree.taiga.io/project/penpot/us/1526)
|
||||||
- Social login redesign [Taiga #2974](https://tree.taiga.io/project/penpot/task/2974)
|
- Social login redesign [Taiga #2974](https://tree.taiga.io/project/penpot/task/2974)
|
||||||
- Add border radius to our artboars [Taiga #2056](https://tree.taiga.io/project/penpot/us/2056)
|
- Add border radius to our artboards [Taiga #2056](https://tree.taiga.io/project/penpot/us/2056)
|
||||||
- Allow send multiple team invitations at once [Taiga #2798](https://tree.taiga.io/project/penpot/us/2798)
|
- Allow send multiple team invitations at once [Taiga #2798](https://tree.taiga.io/project/penpot/us/2798)
|
||||||
- Persist color palette and color picker across refresh [Taiga #1660](https://tree.taiga.io/project/penpot/issue/1660)
|
- Persist color palette and color picker across refresh [Taiga #1660](https://tree.taiga.io/project/penpot/issue/1660)
|
||||||
- Ability to add multiple strokes to a shape [Taiga #2778](https://tree.taiga.io/project/penpot/us/2778)
|
- Ability to add multiple strokes to a shape [Taiga #2778](https://tree.taiga.io/project/penpot/us/2778)
|
||||||
|
@ -42,9 +42,13 @@
|
||||||
- Allow registration with invitation token when registration is disabled
|
- Allow registration with invitation token when registration is disabled
|
||||||
- Add the ability to disable standard, password login [Taiga #2999](https://tree.taiga.io/project/penpot/us/2999)
|
- Add the ability to disable standard, password login [Taiga #2999](https://tree.taiga.io/project/penpot/us/2999)
|
||||||
- Don't stop SVG import when an image cannot be imported [#1531](https://github.com/penpot/penpot/issues/1531)
|
- Don't stop SVG import when an image cannot be imported [#1531](https://github.com/penpot/penpot/issues/1531)
|
||||||
|
- Fix paste shapes while editing text [Taiga #2396](https://tree.taiga.io/project/penpot/issue/2396)
|
||||||
|
|
||||||
### :bug: Bugs fixed
|
### :bug: Bugs fixed
|
||||||
|
|
||||||
|
- Avoid numeric inputs to allow big numbers [Taiga #2858](https://tree.taiga.io/project/penpot/issue/2858)
|
||||||
|
- Fix component contex menu size [Taiga #2480](https://tree.taiga.io/project/penpot/issue/2480)
|
||||||
|
- Add shadow to artboard make it lose the fill [Taiga #3139](https://tree.taiga.io/project/penpot/issue/3139)
|
||||||
- Avoid numeric inputs to change its value without focusing them [Taiga #3140](https://tree.taiga.io/project/penpot/issue/3140)
|
- Avoid numeric inputs to change its value without focusing them [Taiga #3140](https://tree.taiga.io/project/penpot/issue/3140)
|
||||||
- Fix comments modal when changing pages [Taiga #2597](https://tree.taiga.io/project/penpot/issue/2508)
|
- Fix comments modal when changing pages [Taiga #2597](https://tree.taiga.io/project/penpot/issue/2508)
|
||||||
- Copy paste inside a text layer leaves pasted text transparent [Taiga #3096](https://tree.taiga.io/project/penpot/issue/3096)
|
- Copy paste inside a text layer leaves pasted text transparent [Taiga #3096](https://tree.taiga.io/project/penpot/issue/3096)
|
||||||
|
@ -71,10 +75,19 @@
|
||||||
- Fix shift+2 shortcut in MacOS with non-english keyboards [Taiga #3038](https://tree.taiga.io/project/penpot/issue/3038)
|
- Fix shift+2 shortcut in MacOS with non-english keyboards [Taiga #3038](https://tree.taiga.io/project/penpot/issue/3038)
|
||||||
- Some fixes to SVG imports [Taiga #3122](https://tree.taiga.io/project/penpot/issue/3122) [#1720](https://github.com/penpot/penpot/issues/1720) [Taiga #2884](https://tree.taiga.io/project/penpot/issue/2884)
|
- Some fixes to SVG imports [Taiga #3122](https://tree.taiga.io/project/penpot/issue/3122) [#1720](https://github.com/penpot/penpot/issues/1720) [Taiga #2884](https://tree.taiga.io/project/penpot/issue/2884)
|
||||||
- Fix drag guides to delete target area [#1679](https://github.com/penpot/penpot/issues/1679)
|
- Fix drag guides to delete target area [#1679](https://github.com/penpot/penpot/issues/1679)
|
||||||
|
- Fix undo when rotating groups [Taiga #3136](https://tree.taiga.io/project/penpot/issue/3136)
|
||||||
|
- Fix component name in sidebar widget [Taiga #3144](https://tree.taiga.io/project/penpot/issue/3144)
|
||||||
|
|
||||||
### :arrow_up: Deps updates
|
### :arrow_up: Deps updates
|
||||||
### :heart: Community contributions by (Thank you!)
|
### :heart: Community contributions by (Thank you!)
|
||||||
|
|
||||||
|
## 1.12.3-beta
|
||||||
|
|
||||||
|
### :bug: Bugs fixed
|
||||||
|
|
||||||
|
- Fix issue with shift+select to deselect shapes [Taiga #3154](https://tree.taiga.io/project/penpot/issue/3154)
|
||||||
|
- Fix issue with drag-select shapes [Taiga #3165](https://tree.taiga.io/project/penpot/issue/3165)
|
||||||
|
|
||||||
## 1.12.2-beta
|
## 1.12.2-beta
|
||||||
|
|
||||||
### :bug: Bugs fixed
|
### :bug: Bugs fixed
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
{:deps
|
{:deps
|
||||||
{penpot/common {:local/root "../common"}
|
{penpot/common {:local/root "../common"}
|
||||||
|
org.clojure/clojure {:mvn/version "1.10.3"}
|
||||||
org.clojure/core.async {:mvn/version "1.5.648"}
|
org.clojure/core.async {:mvn/version "1.5.648"}
|
||||||
|
|
||||||
;; Logging
|
;; Logging
|
||||||
|
|
|
@ -17,10 +17,11 @@
|
||||||
[app.srepl.dev :as dev]
|
[app.srepl.dev :as dev]
|
||||||
[app.util.blob :as blob]
|
[app.util.blob :as blob]
|
||||||
[app.util.time :as dt]
|
[app.util.time :as dt]
|
||||||
[fipp.edn :refer [pprint]]
|
|
||||||
[clojure.spec.alpha :as s]
|
[clojure.spec.alpha :as s]
|
||||||
|
[clojure.walk :as walk]
|
||||||
[cuerdas.core :as str]
|
[cuerdas.core :as str]
|
||||||
[expound.alpha :as expound]))
|
[expound.alpha :as expound]
|
||||||
|
[fipp.edn :refer [pprint]]))
|
||||||
|
|
||||||
(defn update-file
|
(defn update-file
|
||||||
([system id f] (update-file system id f false))
|
([system id f] (update-file system id f false))
|
||||||
|
@ -66,86 +67,48 @@
|
||||||
(db/insert! conn :file params)
|
(db/insert! conn :file params)
|
||||||
(:id file))))))
|
(:id file))))))
|
||||||
|
|
||||||
(defn verify-files
|
;; (defn check-image-shapes
|
||||||
[system {:keys [age sleep chunk-size max-chunks stop-on-error? verbose?]
|
;; [{:keys [data] :as file} stats]
|
||||||
:or {sleep 1000
|
;; (println "=> analizing file:" (:name file) (:id file))
|
||||||
age "72h"
|
;; (swap! stats update :total-files (fnil inc 0))
|
||||||
chunk-size 10
|
;; (let [affected? (atom false)]
|
||||||
verbose? false
|
;; (walk/prewalk (fn [obj]
|
||||||
stop-on-error? true
|
;; (when (and (map? obj) (= :image (:type obj)))
|
||||||
max-chunks ##Inf}}]
|
;; (when-let [fcolor (some-> obj :fill-color str/upper)]
|
||||||
|
;; (when (or (= fcolor "#B1B2B5")
|
||||||
|
;; (= fcolor "#7B7D85"))
|
||||||
|
;; (reset! affected? true)
|
||||||
|
;; (swap! stats update :affected-shapes (fnil inc 0))
|
||||||
|
;; (println "--> image shape:" ((juxt :id :name :fill-color :fill-opacity) obj)))))
|
||||||
|
;; obj)
|
||||||
|
;; data)
|
||||||
|
;; (when @affected?
|
||||||
|
;; (swap! stats update :affected-files (fnil inc 0)))))
|
||||||
|
|
||||||
|
(defn analyze-files
|
||||||
|
[system {:keys [sleep chunk-size max-chunks on-file]
|
||||||
|
:or {sleep 1000 chunk-size 10 max-chunks ##Inf}}]
|
||||||
|
(let [stats (atom {})]
|
||||||
(letfn [(retrieve-chunk [conn cursor]
|
(letfn [(retrieve-chunk [conn cursor]
|
||||||
(let [sql (str "select id, name, modified_at, data from file "
|
(let [sql (str "select id, name, modified_at, data from file "
|
||||||
" where modified_at > ? and deleted_at is null "
|
" where modified_at < ? and deleted_at is null "
|
||||||
" order by modified_at asc limit ?")
|
" order by modified_at desc limit ?")]
|
||||||
age (if cursor
|
(->> (db/exec! conn [sql cursor chunk-size])
|
||||||
cursor
|
(map #(update % :data blob/decode)))))
|
||||||
(-> (dt/now) (dt/minus age)))]
|
|
||||||
(seq (db/exec! conn [sql age chunk-size]))))
|
|
||||||
|
|
||||||
(validate-item [{:keys [id data modified-at] :as file}]
|
(process-chunk [chunk]
|
||||||
(let [data (blob/decode data)
|
(loop [items chunk]
|
||||||
valid? (s/valid? ::spec.file/data data)]
|
(when-let [item (first items)]
|
||||||
|
(on-file item stats)
|
||||||
|
(recur (rest items)))))]
|
||||||
|
|
||||||
(l/debug :hint "validated file"
|
|
||||||
:file-id id
|
|
||||||
:age (-> (dt/diff modified-at (dt/now))
|
|
||||||
(dt/truncate :minutes)
|
|
||||||
(str)
|
|
||||||
(subs 2)
|
|
||||||
(str/lower))
|
|
||||||
:valid valid?)
|
|
||||||
|
|
||||||
(when (and (not valid?) verbose?)
|
|
||||||
(let [edata (-> (s/explain-data ::spec.file/data data)
|
|
||||||
(update ::s/problems #(take 5 %)))]
|
|
||||||
(binding [s/*explain-out* expound/printer]
|
|
||||||
(l/warn ::l/raw (with-out-str (s/explain-out edata))))))
|
|
||||||
|
|
||||||
(when (and (not valid?) stop-on-error?)
|
|
||||||
(throw (ex-info "penpot/abort" {})))
|
|
||||||
|
|
||||||
valid?))
|
|
||||||
|
|
||||||
(validate-chunk [chunk]
|
|
||||||
(loop [items chunk
|
|
||||||
success 0
|
|
||||||
errored 0]
|
|
||||||
|
|
||||||
(if-let [item (first items)]
|
|
||||||
(if (validate-item item)
|
|
||||||
(recur (rest items) (inc success) errored)
|
|
||||||
(recur (rest items) success (inc errored)))
|
|
||||||
[(:modified-at (last chunk))
|
|
||||||
success
|
|
||||||
errored])))
|
|
||||||
|
|
||||||
(fmt-result [ns ne]
|
|
||||||
{:total (+ ns ne)
|
|
||||||
:errors ne
|
|
||||||
:success ns})
|
|
||||||
|
|
||||||
]
|
|
||||||
|
|
||||||
(try
|
|
||||||
(db/with-atomic [conn (:app.db/pool system)]
|
(db/with-atomic [conn (:app.db/pool system)]
|
||||||
(loop [cursor nil
|
(loop [cursor (dt/now)
|
||||||
chunks 0
|
chunks 0]
|
||||||
success 0
|
(when (< chunks max-chunks)
|
||||||
errors 0]
|
(when-let [chunk (retrieve-chunk conn cursor)]
|
||||||
(if (< chunks max-chunks)
|
(let [cursor (-> chunk last :modified-at)]
|
||||||
(if-let [chunk (retrieve-chunk conn cursor)]
|
(process-chunk chunk)
|
||||||
(let [[cursor success' errors'] (validate-chunk chunk)]
|
|
||||||
(Thread/sleep (inst-ms (dt/duration sleep)))
|
(Thread/sleep (inst-ms (dt/duration sleep)))
|
||||||
(recur cursor
|
(recur cursor (inc chunks))))))
|
||||||
(inc chunks)
|
@stats))))
|
||||||
(+ success success')
|
|
||||||
(+ errors errors')))
|
|
||||||
(fmt-result success errors))
|
|
||||||
(fmt-result success errors))))
|
|
||||||
(catch Throwable cause
|
|
||||||
(when (not= "penpot/abort" (ex-message cause))
|
|
||||||
(throw cause))
|
|
||||||
:error))))
|
|
||||||
|
|
||||||
|
|
|
@ -6,7 +6,8 @@
|
||||||
|
|
||||||
(ns app.common.data
|
(ns app.common.data
|
||||||
"Data manipulation and query helper functions."
|
"Data manipulation and query helper functions."
|
||||||
(:refer-clojure :exclude [read-string hash-map merge name parse-double group-by iteration])
|
(:refer-clojure :exclude [read-string hash-map merge name update-vals
|
||||||
|
parse-double group-by iteration])
|
||||||
#?(:cljs
|
#?(:cljs
|
||||||
(:require-macros [app.common.data]))
|
(:require-macros [app.common.data]))
|
||||||
(:require
|
(:require
|
||||||
|
@ -198,6 +199,23 @@
|
||||||
([mfn coll]
|
([mfn coll]
|
||||||
(into {} (mapm mfn) coll)))
|
(into {} (mapm mfn) coll)))
|
||||||
|
|
||||||
|
;; TEMPORARY COPY of clojure.core/update-vals until we migrate to clojure 1.11
|
||||||
|
|
||||||
|
(defn update-vals
|
||||||
|
"m f => {k (f v) ...}
|
||||||
|
Given a map m and a function f of 1-argument, returns a new map where the keys of m
|
||||||
|
are mapped to result of applying f to the corresponding values of m."
|
||||||
|
[m f]
|
||||||
|
(with-meta
|
||||||
|
(persistent!
|
||||||
|
(reduce-kv (fn [acc k v] (assoc! acc k (f v)))
|
||||||
|
(if #?(:clj (instance? clojure.lang.IEditableCollection m)
|
||||||
|
:cljs (implements? core/IEditableCollection m))
|
||||||
|
(transient m)
|
||||||
|
(transient {}))
|
||||||
|
m))
|
||||||
|
(meta m)))
|
||||||
|
|
||||||
(defn removev
|
(defn removev
|
||||||
"Returns a vector of the items in coll for which (fn item) returns logical false"
|
"Returns a vector of the items in coll for which (fn item) returns logical false"
|
||||||
[fn coll]
|
[fn coll]
|
||||||
|
@ -653,3 +671,13 @@
|
||||||
(recur acc (step k))
|
(recur acc (step k))
|
||||||
acc)))
|
acc)))
|
||||||
acc))))))
|
acc))))))
|
||||||
|
(defn toggle-selection
|
||||||
|
([set value]
|
||||||
|
(toggle-selection set value false))
|
||||||
|
|
||||||
|
([set value toggle?]
|
||||||
|
(if-not toggle?
|
||||||
|
(conj (ordered-set) value)
|
||||||
|
(if (contains? set value)
|
||||||
|
(disj set value)
|
||||||
|
(conj set value)))))
|
||||||
|
|
|
@ -26,7 +26,7 @@
|
||||||
(toString [_]
|
(toString [_]
|
||||||
(str "matrix(" a "," b "," c "," d "," e "," f ")")))
|
(str "matrix(" a "," b "," c "," d "," e "," f ")")))
|
||||||
|
|
||||||
(defn ^boolean matrix?
|
(defn matrix?
|
||||||
"Return true if `v` is Matrix instance."
|
"Return true if `v` is Matrix instance."
|
||||||
[v]
|
[v]
|
||||||
(instance? Matrix v))
|
(instance? Matrix v))
|
||||||
|
@ -57,6 +57,15 @@
|
||||||
(map (comp d/parse-double first)))]
|
(map (comp d/parse-double first)))]
|
||||||
(apply matrix params)))
|
(apply matrix params)))
|
||||||
|
|
||||||
|
(defn close?
|
||||||
|
[m1 m2]
|
||||||
|
(and (mth/close? (.-a m1) (.-a m2))
|
||||||
|
(mth/close? (.-b m1) (.-b m2))
|
||||||
|
(mth/close? (.-c m1) (.-c m2))
|
||||||
|
(mth/close? (.-d m1) (.-d m2))
|
||||||
|
(mth/close? (.-e m1) (.-e m2))
|
||||||
|
(mth/close? (.-f m1) (.-f m2))))
|
||||||
|
|
||||||
(defn multiply
|
(defn multiply
|
||||||
([^Matrix m1 ^Matrix m2]
|
([^Matrix m1 ^Matrix m2]
|
||||||
(let [m1a (.-a m1)
|
(let [m1a (.-a m1)
|
||||||
|
|
|
@ -21,7 +21,7 @@
|
||||||
|
|
||||||
(defn s [{:keys [x y]}] (str "(" x "," y ")"))
|
(defn s [{:keys [x y]}] (str "(" x "," y ")"))
|
||||||
|
|
||||||
(defn ^boolean point?
|
(defn point?
|
||||||
"Return true if `v` is Point instance."
|
"Return true if `v` is Point instance."
|
||||||
[v]
|
[v]
|
||||||
(or (instance? Point v)
|
(or (instance? Point v)
|
||||||
|
@ -33,8 +33,7 @@
|
||||||
(s/def ::point
|
(s/def ::point
|
||||||
(s/and (s/keys :req-un [::x ::y]) point?))
|
(s/and (s/keys :req-un [::x ::y]) point?))
|
||||||
|
|
||||||
|
(defn point-like?
|
||||||
(defn ^boolean point-like?
|
|
||||||
[{:keys [x y] :as v}]
|
[{:keys [x y] :as v}]
|
||||||
(and (map? v)
|
(and (map? v)
|
||||||
(not (nil? x))
|
(not (nil? x))
|
||||||
|
@ -61,6 +60,11 @@
|
||||||
([x y]
|
([x y]
|
||||||
(Point. x y)))
|
(Point. x y)))
|
||||||
|
|
||||||
|
(defn close?
|
||||||
|
[p1 p2]
|
||||||
|
(and (mth/close? (:x p1) (:x p2))
|
||||||
|
(mth/close? (:y p1) (:y p2))))
|
||||||
|
|
||||||
(defn angle->point [{:keys [x y]} angle distance]
|
(defn angle->point [{:keys [x y]} angle distance]
|
||||||
(point
|
(point
|
||||||
(+ x (* distance (mth/cos angle)))
|
(+ x (* distance (mth/cos angle)))
|
||||||
|
|
|
@ -34,6 +34,24 @@
|
||||||
:width width
|
:width width
|
||||||
:height height})))
|
:height height})))
|
||||||
|
|
||||||
|
(defn close-rect?
|
||||||
|
[rect1 rect2]
|
||||||
|
(and (mth/close? (:x rect1) (:x rect2))
|
||||||
|
(mth/close? (:y rect1) (:y rect2))
|
||||||
|
(mth/close? (:width rect1) (:width rect2))
|
||||||
|
(mth/close? (:height rect1) (:height rect2))))
|
||||||
|
|
||||||
|
(defn close-selrect?
|
||||||
|
[selrect1 selrect2]
|
||||||
|
(and (mth/close? (:x selrect1) (:x selrect2))
|
||||||
|
(mth/close? (:y selrect1) (:y selrect2))
|
||||||
|
(mth/close? (:x1 selrect1) (:x1 selrect2))
|
||||||
|
(mth/close? (:y1 selrect1) (:y1 selrect2))
|
||||||
|
(mth/close? (:x2 selrect1) (:x2 selrect2))
|
||||||
|
(mth/close? (:y2 selrect1) (:y2 selrect2))
|
||||||
|
(mth/close? (:width selrect1) (:width selrect2))
|
||||||
|
(mth/close? (:height selrect1) (:height selrect2))))
|
||||||
|
|
||||||
(defn rect->points [{:keys [x y width height]}]
|
(defn rect->points [{:keys [x y width height]}]
|
||||||
(when (d/num? x y)
|
(when (d/num? x y)
|
||||||
(let [width (max width 0.01)
|
(let [width (max width 0.01)
|
||||||
|
|
|
@ -8,8 +8,12 @@
|
||||||
(:require
|
(:require
|
||||||
[app.common.data :as d]
|
[app.common.data :as d]
|
||||||
[app.common.data.macros :as dm]
|
[app.common.data.macros :as dm]
|
||||||
|
[app.common.geom.matrix :as gmt]
|
||||||
|
[app.common.geom.point :as gpt]
|
||||||
[app.common.geom.shapes :as gsh]
|
[app.common.geom.shapes :as gsh]
|
||||||
[app.common.geom.shapes.bool :as gshb]
|
[app.common.geom.shapes.bool :as gshb]
|
||||||
|
[app.common.geom.shapes.rect :as gshr]
|
||||||
|
[app.common.math :as mth]
|
||||||
[app.common.pages :as cp]
|
[app.common.pages :as cp]
|
||||||
[app.common.pages.helpers :as cph]
|
[app.common.pages.helpers :as cph]
|
||||||
[app.common.uuid :as uuid]))
|
[app.common.uuid :as uuid]))
|
||||||
|
@ -379,8 +383,24 @@
|
||||||
generate-operation
|
generate-operation
|
||||||
(fn [operations attr old new]
|
(fn [operations attr old new]
|
||||||
(let [old-val (get old attr)
|
(let [old-val (get old attr)
|
||||||
new-val (get new attr)]
|
new-val (get new attr)
|
||||||
(if (= old-val new-val)
|
|
||||||
|
equal? (cond
|
||||||
|
(and (number? old-val) (number? new-val))
|
||||||
|
(mth/close? old-val new-val)
|
||||||
|
|
||||||
|
(and (gmt/matrix? old-val) (gmt/matrix? new-val))
|
||||||
|
(gmt/close? old-val new-val)
|
||||||
|
|
||||||
|
(= attr :points)
|
||||||
|
(every? #(apply gpt/close? %) (d/zip old-val new-val))
|
||||||
|
|
||||||
|
(= attr :selrect)
|
||||||
|
(gshr/close-selrect? old-val new-val)
|
||||||
|
|
||||||
|
:else
|
||||||
|
(= old-val new-val))]
|
||||||
|
(if equal?
|
||||||
operations
|
operations
|
||||||
(-> operations
|
(-> operations
|
||||||
(update :rops conj {:type :set :attr attr :val new-val :ignore-touched true})
|
(update :rops conj {:type :set :attr attr :val new-val :ignore-touched true})
|
||||||
|
@ -390,8 +410,8 @@
|
||||||
(fn [changes parent]
|
(fn [changes parent]
|
||||||
(let [children (->> parent :shapes (map (d/getf objects)))
|
(let [children (->> parent :shapes (map (d/getf objects)))
|
||||||
resized-parent (cond
|
resized-parent (cond
|
||||||
(empty? children)
|
(empty? children) ;; a parent with no children will be deleted,
|
||||||
changes
|
nil ;; so it does not need resize
|
||||||
|
|
||||||
(= (:type parent) :bool)
|
(= (:type parent) :bool)
|
||||||
(gshb/update-bool-selrect parent children objects)
|
(gshb/update-bool-selrect parent children objects)
|
||||||
|
@ -399,9 +419,9 @@
|
||||||
(= (:type parent) :group)
|
(= (:type parent) :group)
|
||||||
(if (:masked-group? parent)
|
(if (:masked-group? parent)
|
||||||
(gsh/update-mask-selrect parent children)
|
(gsh/update-mask-selrect parent children)
|
||||||
(gsh/update-group-selrect parent children)))
|
(gsh/update-group-selrect parent children)))]
|
||||||
|
(if resized-parent
|
||||||
{rops :rops uops :uops}
|
(let [{rops :rops uops :uops}
|
||||||
(reduce #(generate-operation %1 %2 parent resized-parent)
|
(reduce #(generate-operation %1 %2 parent resized-parent)
|
||||||
{:rops [] :uops []}
|
{:rops [] :uops []}
|
||||||
(keys parent))
|
(keys parent))
|
||||||
|
@ -413,7 +433,8 @@
|
||||||
(if (seq rops)
|
(if (seq rops)
|
||||||
(-> changes
|
(-> changes
|
||||||
(update :redo-changes conj (assoc change :operations rops))
|
(update :redo-changes conj (assoc change :operations rops))
|
||||||
(update :undo-changes conj (assoc change :operations uops)))
|
(update :undo-changes d/preconj (assoc change :operations uops)))
|
||||||
|
changes))
|
||||||
changes)))]
|
changes)))]
|
||||||
|
|
||||||
(-> (reduce resize-parent changes all-parents)
|
(-> (reduce resize-parent changes all-parents)
|
||||||
|
|
|
@ -9,7 +9,7 @@
|
||||||
[app.common.colors :as clr]
|
[app.common.colors :as clr]
|
||||||
[app.common.uuid :as uuid]))
|
[app.common.uuid :as uuid]))
|
||||||
|
|
||||||
(def file-version 16)
|
(def file-version 17)
|
||||||
(def default-color clr/gray-20)
|
(def default-color clr/gray-20)
|
||||||
(def root uuid/zero)
|
(def root uuid/zero)
|
||||||
|
|
||||||
|
|
|
@ -17,24 +17,28 @@
|
||||||
;; GENERIC SHAPE SELECTORS AND PREDICATES
|
;; GENERIC SHAPE SELECTORS AND PREDICATES
|
||||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||||
|
|
||||||
(defn ^boolean root-frame?
|
(defn root-frame?
|
||||||
[{:keys [id type]}]
|
[{:keys [id type]}]
|
||||||
(and (= type :frame)
|
(and (= type :frame)
|
||||||
(= id uuid/zero)))
|
(= id uuid/zero)))
|
||||||
|
|
||||||
(defn ^boolean frame-shape?
|
(defn frame-shape?
|
||||||
[{:keys [type]}]
|
[{:keys [type]}]
|
||||||
(= type :frame))
|
(= type :frame))
|
||||||
|
|
||||||
(defn ^boolean group-shape?
|
(defn group-shape?
|
||||||
[{:keys [type]}]
|
[{:keys [type]}]
|
||||||
(= type :group))
|
(= type :group))
|
||||||
|
|
||||||
(defn ^boolean text-shape?
|
(defn text-shape?
|
||||||
[{:keys [type]}]
|
[{:keys [type]}]
|
||||||
(= type :text))
|
(= type :text))
|
||||||
|
|
||||||
(defn ^boolean unframed-shape?
|
(defn image-shape?
|
||||||
|
[{:keys [type]}]
|
||||||
|
(= type :image))
|
||||||
|
|
||||||
|
(defn unframed-shape?
|
||||||
"Checks if it's a non-frame shape in the top level."
|
"Checks if it's a non-frame shape in the top level."
|
||||||
[shape]
|
[shape]
|
||||||
(and (not (frame-shape? shape))
|
(and (not (frame-shape? shape))
|
||||||
|
@ -214,7 +218,7 @@
|
||||||
([libraries library-id component-id]
|
([libraries library-id component-id]
|
||||||
(get-in libraries [library-id :data :components component-id])))
|
(get-in libraries [library-id :data :components component-id])))
|
||||||
|
|
||||||
(defn ^boolean is-main-of?
|
(defn is-main-of?
|
||||||
[shape-main shape-inst]
|
[shape-main shape-inst]
|
||||||
(and (:shape-ref shape-inst)
|
(and (:shape-ref shape-inst)
|
||||||
(or (= (:shape-ref shape-inst) (:id shape-main))
|
(or (= (:shape-ref shape-inst) (:id shape-main))
|
||||||
|
|
|
@ -10,26 +10,26 @@
|
||||||
[app.common.geom.matrix :as gmt]
|
[app.common.geom.matrix :as gmt]
|
||||||
[app.common.geom.shapes :as gsh]
|
[app.common.geom.shapes :as gsh]
|
||||||
[app.common.geom.shapes.path :as gsp]
|
[app.common.geom.shapes.path :as gsp]
|
||||||
|
[app.common.logging :as l]
|
||||||
[app.common.math :as mth]
|
[app.common.math :as mth]
|
||||||
[app.common.pages :as cp]
|
[app.common.pages :as cp]
|
||||||
[app.common.uuid :as uuid]))
|
[app.common.pages.helpers :as cph]
|
||||||
|
[app.common.uuid :as uuid]
|
||||||
|
[cuerdas.core :as str]))
|
||||||
|
|
||||||
;; TODO: revisit this and rename to file-migrations
|
;; TODO: revisit this and rename to file-migrations
|
||||||
|
|
||||||
(defmulti migrate :version)
|
(defmulti migrate :version)
|
||||||
|
|
||||||
(defn migrate-data
|
(defn migrate-data
|
||||||
([data]
|
([data] (migrate-data data cp/file-version))
|
||||||
(if (= (:version data) cp/file-version)
|
([data to-version]
|
||||||
|
(if (= (:version data) to-version)
|
||||||
data
|
data
|
||||||
(reduce #(migrate-data %1 %2 (inc %2))
|
(let [migrate-fn #(do
|
||||||
data
|
(l/trace :hint "migrate file" :id (:id %) :version-from %2 :version-to (inc %2))
|
||||||
(range (:version data 0) cp/file-version))))
|
(migrate (assoc %1 :version (inc %2))))]
|
||||||
|
(reduce migrate-fn data (range (:version data 0) to-version))))))
|
||||||
([data _ to-version]
|
|
||||||
(-> data
|
|
||||||
(assoc :version to-version)
|
|
||||||
(migrate))))
|
|
||||||
|
|
||||||
(defn migrate-file
|
(defn migrate-file
|
||||||
[file]
|
[file]
|
||||||
|
@ -45,17 +45,16 @@
|
||||||
;; Ensure that all :shape attributes on shapes are vectors.
|
;; Ensure that all :shape attributes on shapes are vectors.
|
||||||
(defmethod migrate 2
|
(defmethod migrate 2
|
||||||
[data]
|
[data]
|
||||||
(letfn [(update-object [_ object]
|
(letfn [(update-object [object]
|
||||||
(d/update-when object :shapes
|
(d/update-when object :shapes
|
||||||
(fn [shapes]
|
(fn [shapes]
|
||||||
(if (seq? shapes)
|
(if (seq? shapes)
|
||||||
(into [] shapes)
|
(into [] shapes)
|
||||||
shapes))))
|
shapes))))
|
||||||
|
(update-page [page]
|
||||||
|
(update page :objects d/update-vals update-object))]
|
||||||
|
|
||||||
(update-page [_ page]
|
(update data :pages-index d/update-vals update-page)))
|
||||||
(update page :objects #(d/mapm update-object %)))]
|
|
||||||
|
|
||||||
(update data :pages-index #(d/mapm update-page %))))
|
|
||||||
|
|
||||||
;; Changes paths formats
|
;; Changes paths formats
|
||||||
(defmethod migrate 3
|
(defmethod migrate 3
|
||||||
|
@ -89,7 +88,7 @@
|
||||||
(empty? (:points shape))
|
(empty? (:points shape))
|
||||||
(assoc :points (gsh/rect->points (:selrect shape))))))
|
(assoc :points (gsh/rect->points (:selrect shape))))))
|
||||||
|
|
||||||
(update-object [_ object]
|
(update-object [object]
|
||||||
(cond-> object
|
(cond-> object
|
||||||
(= :curve (:type object))
|
(= :curve (:type object))
|
||||||
(assoc :type :path)
|
(assoc :type :path)
|
||||||
|
@ -97,25 +96,22 @@
|
||||||
(#{:curve :path} (:type object))
|
(#{:curve :path} (:type object))
|
||||||
(migrate-path)
|
(migrate-path)
|
||||||
|
|
||||||
(= :frame (:type object))
|
(cph/frame-shape? object)
|
||||||
(fix-frames-selrects)
|
(fix-frames-selrects)
|
||||||
|
|
||||||
(and (empty? (:points object)) (not= (:id object) uuid/zero))
|
(and (empty? (:points object)) (not= (:id object) uuid/zero))
|
||||||
(fix-empty-points)
|
(fix-empty-points)
|
||||||
|
|
||||||
:always
|
|
||||||
(->
|
|
||||||
;; Setup an empty transformation to re-calculate selrects
|
;; Setup an empty transformation to re-calculate selrects
|
||||||
;; and points data
|
;; and points data
|
||||||
(assoc :modifiers {:displacement (gmt/matrix)})
|
:always
|
||||||
(gsh/transform-shape))
|
(-> (assoc :modifiers {:displacement (gmt/matrix)})
|
||||||
|
(gsh/transform-shape))))
|
||||||
|
|
||||||
))
|
(update-page [page]
|
||||||
|
(update page :objects d/update-vals update-object))]
|
||||||
|
|
||||||
(update-page [_ page]
|
(update data :pages-index d/update-vals update-page)))
|
||||||
(update page :objects #(d/mapm update-object %)))]
|
|
||||||
|
|
||||||
(update data :pages-index #(d/mapm update-page %))))
|
|
||||||
|
|
||||||
;; We did rollback version 4 migration.
|
;; We did rollback version 4 migration.
|
||||||
;; Keep this in order to remember the next version to be 5
|
;; Keep this in order to remember the next version to be 5
|
||||||
|
@ -124,20 +120,21 @@
|
||||||
;; Put the id of the local file in :component-file in instances of local components
|
;; Put the id of the local file in :component-file in instances of local components
|
||||||
(defmethod migrate 5
|
(defmethod migrate 5
|
||||||
[data]
|
[data]
|
||||||
(letfn [(update-object [_ object]
|
(letfn [(update-object [object]
|
||||||
(if (and (some? (:component-id object))
|
(if (and (some? (:component-id object))
|
||||||
(nil? (:component-file object)))
|
(nil? (:component-file object)))
|
||||||
(assoc object :component-file (:id data))
|
(assoc object :component-file (:id data))
|
||||||
object))
|
object))
|
||||||
|
|
||||||
(update-page [_ page]
|
(update-page [page]
|
||||||
(update page :objects #(d/mapm update-object %)))]
|
(update page :objects d/update-vals update-object))]
|
||||||
|
|
||||||
(update data :pages-index #(d/mapm update-page %))))
|
(update data :pages-index d/update-vals update-page)))
|
||||||
|
|
||||||
(defn fix-line-paths
|
(defmethod migrate 6
|
||||||
"Fixes issues with selrect/points for shapes with width/height = 0 (line-like paths)"
|
[data]
|
||||||
[_ shape]
|
;; Fixes issues with selrect/points for shapes with width/height = 0 (line-like paths)"
|
||||||
|
(letfn [(fix-line-paths [shape]
|
||||||
(if (= (:type shape) :path)
|
(if (= (:type shape) :path)
|
||||||
(let [{:keys [width height]} (gsh/points->rect (:points shape))]
|
(let [{:keys [width height]} (gsh/points->rect (:points shape))]
|
||||||
(if (or (mth/almost-zero? width) (mth/almost-zero? height))
|
(if (or (mth/almost-zero? width) (mth/almost-zero? height))
|
||||||
|
@ -153,32 +150,25 @@
|
||||||
shape))
|
shape))
|
||||||
shape))
|
shape))
|
||||||
|
|
||||||
|
(update-container [container]
|
||||||
(defmethod migrate 6
|
(update container :objects d/update-vals fix-line-paths))]
|
||||||
[data]
|
|
||||||
(letfn [(update-container [_ container]
|
|
||||||
(-> container
|
|
||||||
(update :objects #(d/mapm fix-line-paths %))))]
|
|
||||||
|
|
||||||
(-> data
|
(-> data
|
||||||
(update :components #(d/mapm update-container %))
|
(update :pages-index d/update-vals update-container)
|
||||||
(update :pages-index #(d/mapm update-container %)))))
|
(update :components d/update-vals update-container))))
|
||||||
|
|
||||||
|
|
||||||
;; Remove interactions pointing to deleted frames
|
;; Remove interactions pointing to deleted frames
|
||||||
(defmethod migrate 7
|
(defmethod migrate 7
|
||||||
[data]
|
[data]
|
||||||
(letfn [(update-object [page _ object]
|
(letfn [(update-object [page object]
|
||||||
(d/update-when object :interactions
|
(d/update-when object :interactions
|
||||||
(fn [interactions]
|
(fn [interactions]
|
||||||
(filterv #(get-in page [:objects (:destination %)])
|
(filterv #(get-in page [:objects (:destination %)]) interactions))))
|
||||||
interactions))))
|
|
||||||
|
|
||||||
(update-page [_ page]
|
(update-page [page]
|
||||||
(update page :objects #(d/mapm (partial update-object page) %)))]
|
(update page :objects d/update-vals (partial update-object page)))]
|
||||||
|
|
||||||
(update data :pages-index #(d/mapm update-page %))))
|
|
||||||
|
|
||||||
|
(update data :pages-index d/update-vals update-page)))
|
||||||
|
|
||||||
;; Remove groups without any shape, both in pages and components
|
;; Remove groups without any shape, both in pages and components
|
||||||
|
|
||||||
|
@ -210,7 +200,7 @@
|
||||||
[(count deleted)
|
[(count deleted)
|
||||||
(d/mapm #(clean-parents %2 deleted) result)]))))
|
(d/mapm #(clean-parents %2 deleted) result)]))))
|
||||||
|
|
||||||
(clean-container [_ container]
|
(clean-container [container]
|
||||||
(loop [n 0
|
(loop [n 0
|
||||||
objects (:objects container)]
|
objects (:objects container)]
|
||||||
(let [[deleted objects] (clean-objects objects)]
|
(let [[deleted objects] (clean-objects objects)]
|
||||||
|
@ -219,8 +209,8 @@
|
||||||
(assoc container :objects objects)))))]
|
(assoc container :objects objects)))))]
|
||||||
|
|
||||||
(-> data
|
(-> data
|
||||||
(update :pages-index #(d/mapm clean-container %))
|
(update :pages-index d/update-vals clean-container)
|
||||||
(d/update-when :components #(d/mapm clean-container %)))))
|
(update :components d/update-vals clean-container))))
|
||||||
|
|
||||||
(defmethod migrate 9
|
(defmethod migrate 9
|
||||||
[data]
|
[data]
|
||||||
|
@ -252,35 +242,35 @@
|
||||||
|
|
||||||
(defmethod migrate 10
|
(defmethod migrate 10
|
||||||
[data]
|
[data]
|
||||||
(letfn [(update-page [_ page]
|
(letfn [(update-page [page]
|
||||||
(d/update-in-when page [:objects uuid/zero] dissoc :points :selrect))]
|
(d/update-in-when page [:objects uuid/zero] dissoc :points :selrect))]
|
||||||
(update data :pages-index #(d/mapm update-page %))))
|
(update data :pages-index d/update-vals update-page)))
|
||||||
|
|
||||||
(defmethod migrate 11
|
(defmethod migrate 11
|
||||||
[data]
|
[data]
|
||||||
(letfn [(update-object [objects _id shape]
|
(letfn [(update-object [objects shape]
|
||||||
(if (= :frame (:type shape))
|
(if (cph/frame-shape? shape)
|
||||||
(d/update-when shape :shapes (fn [shapes]
|
(d/update-when shape :shapes (fn [shapes]
|
||||||
(filterv (fn [id] (contains? objects id)) shapes)))
|
(filterv (fn [id] (contains? objects id)) shapes)))
|
||||||
shape))
|
shape))
|
||||||
|
|
||||||
(update-page [_ page]
|
(update-page [page]
|
||||||
(update page :objects #(d/mapm (partial update-object %) %)))]
|
(update page :objects (fn [objects]
|
||||||
|
(d/update-vals objects (partial update-object objects)))))]
|
||||||
(update data :pages-index #(d/mapm update-page %))))
|
|
||||||
|
|
||||||
|
(update data :pages-index d/update-vals update-page)))
|
||||||
|
|
||||||
(defmethod migrate 12
|
(defmethod migrate 12
|
||||||
[data]
|
[data]
|
||||||
(letfn [(update-grid [_key grid]
|
(letfn [(update-grid [grid]
|
||||||
(cond-> grid
|
(cond-> grid
|
||||||
(= :auto (:size grid))
|
(= :auto (:size grid))
|
||||||
(assoc :size nil)))
|
(assoc :size nil)))
|
||||||
|
|
||||||
(update-page [_id page]
|
(update-page [page]
|
||||||
(d/update-in-when page [:options :saved-grids] #(d/mapm update-grid %)))]
|
(d/update-in-when page [:options :saved-grids] d/update-vals update-grid))]
|
||||||
|
|
||||||
(update data :pages-index #(d/mapm update-page %))))
|
(update data :pages-index d/update-vals update-page)))
|
||||||
|
|
||||||
;; Add rx and ry to images
|
;; Add rx and ry to images
|
||||||
(defmethod migrate 13
|
(defmethod migrate 13
|
||||||
|
@ -291,43 +281,64 @@
|
||||||
(assoc :rx 0)
|
(assoc :rx 0)
|
||||||
(assoc :ry 0))
|
(assoc :ry 0))
|
||||||
shape))
|
shape))
|
||||||
(update-object [_ object]
|
|
||||||
|
(update-object [object]
|
||||||
(cond-> object
|
(cond-> object
|
||||||
(= :image (:type object))
|
(cph/image-shape? object)
|
||||||
(fix-radius)))
|
(fix-radius)))
|
||||||
|
|
||||||
(update-page [_ page]
|
(update-page [page]
|
||||||
(update page :objects #(d/mapm update-object %)))]
|
(update page :objects d/update-vals update-object))]
|
||||||
|
|
||||||
(update data :pages-index #(d/mapm update-page %))))
|
(update data :pages-index d/update-vals update-page)))
|
||||||
|
|
||||||
(defn set-fills
|
(defmethod migrate 14
|
||||||
[shape]
|
[data]
|
||||||
|
(letfn [(process-shape [shape]
|
||||||
|
(let [fill-color (str/upper (:fill-color shape))
|
||||||
|
fill-opacity (:fill-opacity shape)]
|
||||||
|
(cond-> shape
|
||||||
|
(and (= 1 fill-opacity)
|
||||||
|
(or (= "#B1B2B5" fill-color)
|
||||||
|
(= "#7B7D85" fill-color)))
|
||||||
|
(dissoc :fill-color :fill-opacity))))
|
||||||
|
|
||||||
|
(update-container [{:keys [objects] :as container}]
|
||||||
|
(loop [objects objects
|
||||||
|
shapes (->> (vals objects)
|
||||||
|
(filter cph/image-shape?))]
|
||||||
|
(if-let [shape (first shapes)]
|
||||||
|
(let [{:keys [id frame-id] :as shape'} (process-shape shape)]
|
||||||
|
(if (identical? shape shape')
|
||||||
|
(recur objects (rest shapes))
|
||||||
|
(recur (-> objects
|
||||||
|
(assoc id shape')
|
||||||
|
(d/update-when frame-id dissoc :thumbnail))
|
||||||
|
(rest shapes))))
|
||||||
|
(assoc container :objects objects))))]
|
||||||
|
|
||||||
|
(-> data
|
||||||
|
(update :pages-index d/update-vals update-container)
|
||||||
|
(update :components d/update-vals update-container))))
|
||||||
|
|
||||||
|
|
||||||
|
(defmethod migrate 15 [data] data)
|
||||||
|
|
||||||
|
;; Add fills and strokes
|
||||||
|
(defmethod migrate 16
|
||||||
|
[data]
|
||||||
|
(letfn [(assign-fills [shape]
|
||||||
(let [attrs {:fill-color (:fill-color shape)
|
(let [attrs {:fill-color (:fill-color shape)
|
||||||
:fill-color-gradient (:fill-color-gradient shape)
|
:fill-color-gradient (:fill-color-gradient shape)
|
||||||
:fill-color-ref-file (:fill-color-ref-file shape)
|
:fill-color-ref-file (:fill-color-ref-file shape)
|
||||||
:fill-color-ref-id (:fill-color-ref-id shape)
|
:fill-color-ref-id (:fill-color-ref-id shape)
|
||||||
:fill-opacity (:fill-opacity shape)}
|
:fill-opacity (:fill-opacity shape)}
|
||||||
|
|
||||||
clean-attrs (d/without-nils attrs)]
|
clean-attrs (d/without-nils attrs)]
|
||||||
(cond-> shape
|
(cond-> shape
|
||||||
(d/not-empty? clean-attrs)
|
(d/not-empty? clean-attrs)
|
||||||
(assoc :fills [clean-attrs]))))
|
(assoc :fills [clean-attrs]))))
|
||||||
|
|
||||||
;; Add fills to shapes
|
(assign-strokes [shape]
|
||||||
(defmethod migrate 14
|
|
||||||
[data]
|
|
||||||
(letfn [(update-object [_ object]
|
|
||||||
(cond-> object
|
|
||||||
(and (not (= :text (:type object))) (nil? (:fills object)))
|
|
||||||
(set-fills)))
|
|
||||||
|
|
||||||
(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)
|
(let [attrs {:stroke-style (:stroke-style shape)
|
||||||
:stroke-alignment (:stroke-alignment shape)
|
:stroke-alignment (:stroke-alignment shape)
|
||||||
:stroke-width (:stroke-width shape)
|
:stroke-width (:stroke-width shape)
|
||||||
|
@ -338,36 +349,56 @@
|
||||||
:stroke-color-gradient (:stroke-color-gradient shape)
|
:stroke-color-gradient (:stroke-color-gradient shape)
|
||||||
:stroke-cap-start (:stroke-cap-start shape)
|
:stroke-cap-start (:stroke-cap-start shape)
|
||||||
:stroke-cap-end (:stroke-cap-end shape)}
|
:stroke-cap-end (:stroke-cap-end shape)}
|
||||||
|
|
||||||
clean-attrs (d/without-nils attrs)]
|
clean-attrs (d/without-nils attrs)]
|
||||||
(cond-> shape
|
(cond-> shape
|
||||||
(d/not-empty? clean-attrs)
|
(d/not-empty? clean-attrs)
|
||||||
(assoc :strokes [clean-attrs]))))
|
(assoc :strokes [clean-attrs]))))
|
||||||
|
|
||||||
;; Add strokes to shapes
|
(update-object [object]
|
||||||
(defmethod migrate 15
|
|
||||||
[data]
|
|
||||||
(letfn [(update-object [_ object]
|
|
||||||
(cond-> object
|
(cond-> object
|
||||||
(and (not (= :text (:type object))) (nil? (:strokes object)))
|
(and (not (cph/text-shape? object))
|
||||||
(set-strokes)))
|
(not (contains? object :strokes)))
|
||||||
|
(assign-strokes)
|
||||||
|
|
||||||
(update-page [_ page]
|
(and (not (cph/text-shape? object))
|
||||||
(update page :objects #(d/mapm update-object %)))]
|
(not (contains? object :fills)))
|
||||||
(update data :pages-index #(d/mapm update-page %))))
|
(assign-fills)))
|
||||||
|
|
||||||
;; Add fills and strokes to components
|
(update-container [container]
|
||||||
|
(update container :objects d/update-vals update-object))]
|
||||||
|
|
||||||
(defmethod migrate 16
|
|
||||||
[data]
|
|
||||||
(letfn [(update-object [_ object]
|
|
||||||
(cond-> object
|
|
||||||
(and (not (= :text (:type object))) (nil? (:strokes object)))
|
|
||||||
(set-strokes)
|
|
||||||
|
|
||||||
(and (not (= :text (:type object))) (nil? (:fills object)))
|
|
||||||
(set-fills)))
|
|
||||||
(update-container [_ container]
|
|
||||||
(update container :objects #(d/mapm update-object %)))]
|
|
||||||
(-> data
|
(-> data
|
||||||
(update :components #(d/mapm update-container %)))))
|
(update :pages-index d/update-vals update-container)
|
||||||
|
(update :components d/update-vals update-container))))
|
||||||
|
|
||||||
|
(defmethod migrate 17
|
||||||
|
[data]
|
||||||
|
(letfn [(affected-object? [object]
|
||||||
|
(and (cph/image-shape? object)
|
||||||
|
(some? (:fills object))
|
||||||
|
(= 1 (count (:fills object)))
|
||||||
|
(some? (:fill-color object))
|
||||||
|
(some? (:fill-opacity object))
|
||||||
|
(let [color-old (str/upper (:fill-color object))
|
||||||
|
color-new (str/upper (get-in object [:fills 0 :fill-color]))
|
||||||
|
opacity-old (:fill-opacity object)
|
||||||
|
opacity-new (get-in object [:fills 0 :fill-opacity])]
|
||||||
|
(and (= color-old color-new)
|
||||||
|
(or (= "#B1B2B5" color-old)
|
||||||
|
(= "#7B7D85" color-old))
|
||||||
|
(= 1 opacity-old opacity-new)))))
|
||||||
|
|
||||||
|
(update-object [object]
|
||||||
|
(cond-> object
|
||||||
|
(affected-object? object)
|
||||||
|
(assoc :fills [])))
|
||||||
|
|
||||||
|
(update-container [container]
|
||||||
|
(update container :objects d/update-vals update-object))]
|
||||||
|
|
||||||
|
(-> data
|
||||||
|
(update :pages-index d/update-vals update-container)
|
||||||
|
(update :components d/update-vals update-container))))
|
||||||
|
|
||||||
|
;; TODO: pending to do a migration for delete already not used fill
|
||||||
|
;; and stroke props. This should be done for >1.14.x version.
|
||||||
|
|
|
@ -63,16 +63,16 @@
|
||||||
(filter match?)
|
(filter match?)
|
||||||
(seq))))
|
(seq))))
|
||||||
|
|
||||||
(defn ^boolean is-text-node?
|
(defn is-text-node?
|
||||||
[node]
|
[node]
|
||||||
(and (string? (:text node))
|
(and (string? (:text node))
|
||||||
(not= (:text node) "")))
|
(not= (:text node) "")))
|
||||||
|
|
||||||
(defn ^boolean is-paragraph-node?
|
(defn is-paragraph-node?
|
||||||
[node]
|
[node]
|
||||||
(= "paragraph" (:type node)))
|
(= "paragraph" (:type node)))
|
||||||
|
|
||||||
(defn ^boolean is-root-node?
|
(defn is-root-node?
|
||||||
[node]
|
[node]
|
||||||
(= "root" (:type node)))
|
(= "root" (:type node)))
|
||||||
|
|
||||||
|
|
|
@ -38,7 +38,7 @@
|
||||||
:components {}
|
:components {}
|
||||||
:version 7}
|
:version 7}
|
||||||
|
|
||||||
res (cpm/migrate-data data nil 8)]
|
res (cpm/migrate-data data 8)]
|
||||||
|
|
||||||
;; (pprint data)
|
;; (pprint data)
|
||||||
;; (pprint res)
|
;; (pprint res)
|
||||||
|
@ -81,7 +81,7 @@
|
||||||
(let [id (uuid/custom 1 2)]
|
(let [id (uuid/custom 1 2)]
|
||||||
(into [] (remove #(= id %)) shapes)))))
|
(into [] (remove #(= id %)) shapes)))))
|
||||||
|
|
||||||
res (cpm/migrate-data data nil 8)]
|
res (cpm/migrate-data data 8)]
|
||||||
|
|
||||||
;; (pprint res)
|
;; (pprint res)
|
||||||
;; (pprint expect)
|
;; (pprint expect)
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
{:paths ["src" "vendor" "resources" "test"]
|
{:paths ["src" "vendor" "resources" "test"]
|
||||||
:deps
|
:deps
|
||||||
{penpot/common {:local/root "../common"}
|
{penpot/common {:local/root "../common"}
|
||||||
|
org.clojure/clojure {:mvn/version "1.10.3"}
|
||||||
binaryage/devtools {:mvn/version "RELEASE"}
|
binaryage/devtools {:mvn/version "RELEASE"}
|
||||||
metosin/reitit-core {:mvn/version "0.5.16"}
|
metosin/reitit-core {:mvn/version "0.5.16"}
|
||||||
funcool/beicon {:mvn/version "2021.07.05-1"}
|
funcool/beicon {:mvn/version "2021.07.05-1"}
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
{penpot/common
|
{penpot/common
|
||||||
{:local/root "../common"}
|
{:local/root "../common"}
|
||||||
|
|
||||||
|
org.clojure/clojure {:mvn/version "1.10.3"}
|
||||||
binaryage/devtools {:mvn/version "RELEASE"}
|
binaryage/devtools {:mvn/version "RELEASE"}
|
||||||
metosin/reitit-core {:mvn/version "0.5.15"}
|
metosin/reitit-core {:mvn/version "0.5.15"}
|
||||||
|
|
||||||
|
|
|
@ -1209,7 +1209,8 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
.modal-container {
|
.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-repeat: no-repeat;
|
||||||
background-position: 10% 50px, 90% 50px;
|
background-position: 10% 50px, 90% 50px;
|
||||||
background-size: 65px;
|
background-size: 65px;
|
||||||
|
@ -1236,8 +1237,18 @@
|
||||||
--checkbox-border-radius: 3px;
|
--checkbox-border-radius: 3px;
|
||||||
--dropdown-option-background-color: rgba(0, 195, 139, 1);
|
--dropdown-option-background-color: rgba(0, 195, 139, 1);
|
||||||
--dropdown-option-active-background-color: rgba(0, 138, 98, 1);
|
--dropdown-option-active-background-color: rgba(0, 138, 98, 1);
|
||||||
--invalid-field-background-color: rgba(238.51780000000002, 205.7178, 204.11780000000002, 1);
|
--invalid-field-background-color: rgba(
|
||||||
--message-fail-background-color: rgba(238.51780000000002, 205.7178, 204.11780000000002, 1);
|
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);
|
--message-success-background-color: rgba(171, 232, 197, 1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -523,6 +523,12 @@
|
||||||
right: 0.5rem;
|
right: 0.5rem;
|
||||||
left: unset;
|
left: unset;
|
||||||
top: 0;
|
top: 0;
|
||||||
|
|
||||||
|
.context-menu-action {
|
||||||
|
overflow-wrap: break-word;
|
||||||
|
min-width: 223px;
|
||||||
|
white-space: break-spaces;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -194,7 +194,8 @@
|
||||||
(ptk/reify ::initialize-page
|
(ptk/reify ::initialize-page
|
||||||
ptk/WatchEvent
|
ptk/WatchEvent
|
||||||
(watch [_ state _]
|
(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])]
|
(let [default-page-id (get-in state [:workspace-data :pages 0])]
|
||||||
(rx/of (go-to-page default-page-id)))))
|
(rx/of (go-to-page default-page-id)))))
|
||||||
|
|
||||||
|
@ -1356,6 +1357,9 @@
|
||||||
edit-id (get-in state [:workspace-local :edition])
|
edit-id (get-in state [:workspace-local :edition])
|
||||||
is-editing-text? (and edit-id (= :text (get-in objects [edit-id :type])))]
|
is-editing-text? (and edit-id (= :text (get-in objects [edit-id :type])))]
|
||||||
|
|
||||||
|
;; 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
|
(cond
|
||||||
(and (string? text-data)
|
(and (string? text-data)
|
||||||
(str/includes? text-data "<svg"))
|
(str/includes? text-data "<svg"))
|
||||||
|
@ -1369,13 +1373,12 @@
|
||||||
(rx/filter #(= :copied-shapes (:type %)))
|
(rx/filter #(= :copied-shapes (:type %)))
|
||||||
(rx/map #(paste-shape % in-viewport?)))
|
(rx/map #(paste-shape % in-viewport?)))
|
||||||
|
|
||||||
;; Some paste events can be fired while we're editing a text
|
(string? text-data)
|
||||||
;; we forbid that scenario so the default behaviour is executed
|
|
||||||
(and (string? text-data) (not is-editing-text?))
|
|
||||||
(rx/of (paste-text text-data))
|
(rx/of (paste-text text-data))
|
||||||
|
|
||||||
:else
|
:else
|
||||||
(rx/empty)))
|
(rx/empty))))
|
||||||
|
|
||||||
(catch :default err
|
(catch :default err
|
||||||
(js/console.error "Clipboard error:" err))))))
|
(js/console.error "Clipboard error:" err))))))
|
||||||
|
|
||||||
|
|
|
@ -14,6 +14,7 @@
|
||||||
[app.common.spec.change :as spec.change]
|
[app.common.spec.change :as spec.change]
|
||||||
[app.common.spec.file :as spec.file]
|
[app.common.spec.file :as spec.file]
|
||||||
[app.common.uuid :as uuid]
|
[app.common.uuid :as uuid]
|
||||||
|
[app.config :as cfg]
|
||||||
[app.main.data.dashboard :as dd]
|
[app.main.data.dashboard :as dd]
|
||||||
[app.main.data.events :as ev]
|
[app.main.data.events :as ev]
|
||||||
[app.main.data.fonts :as df]
|
[app.main.data.fonts :as df]
|
||||||
|
@ -653,6 +654,9 @@
|
||||||
|
|
||||||
frame-changes (->> stream
|
frame-changes (->> stream
|
||||||
(rx/filter dch/commit-changes?)
|
(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/filter (comp not thumbnail-change?))
|
||||||
(rx/with-latest-from objects-stream)
|
(rx/with-latest-from objects-stream)
|
||||||
(rx/map extract-frame-changes)
|
(rx/map extract-frame-changes)
|
||||||
|
@ -678,3 +682,26 @@
|
||||||
(rx/buffer-until (->> frame-changes (rx/debounce 1000)))
|
(rx/buffer-until (->> frame-changes (rx/debounce 1000)))
|
||||||
(rx/flat-map #(reduce set/union %))
|
(rx/flat-map #(reduce set/union %))
|
||||||
(rx/map #(update-frame-thumbnail %)))))))))
|
(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))))))
|
||||||
|
|
|
@ -110,7 +110,10 @@
|
||||||
(rx/dedupe)
|
(rx/dedupe)
|
||||||
(rx/map #(select-shapes-by-current-selrect preserve? ignore-groups?))))
|
(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)
|
;; --- Toggle shape's selection status (selected or deselected)
|
||||||
|
|
||||||
|
@ -123,13 +126,7 @@
|
||||||
(ptk/reify ::select-shape
|
(ptk/reify ::select-shape
|
||||||
ptk/UpdateEvent
|
ptk/UpdateEvent
|
||||||
(update [_ state]
|
(update [_ state]
|
||||||
(update-in state [:workspace-local :selected]
|
(update-in state [:workspace-local :selected] d/toggle-selection id toggle?))
|
||||||
(fn [selected]
|
|
||||||
(if-not toggle?
|
|
||||||
(conj (d/ordered-set) id)
|
|
||||||
(if (contains? selected id)
|
|
||||||
(disj selected id)
|
|
||||||
(conj selected id))))))
|
|
||||||
|
|
||||||
ptk/WatchEvent
|
ptk/WatchEvent
|
||||||
(watch [_ state _]
|
(watch [_ state _]
|
||||||
|
@ -506,6 +503,7 @@
|
||||||
|
|
||||||
id-duplicated (when (= (count selected) 1) (first selected))]
|
id-duplicated (when (= (count selected) 1) (first selected))]
|
||||||
|
|
||||||
|
;; Warning: This order is important for the focus mode.
|
||||||
(rx/of (dch/commit-changes changes)
|
(rx/of (dch/commit-changes changes)
|
||||||
(select-shapes selected)
|
(select-shapes selected)
|
||||||
(memorize-duplicated id-original id-duplicated)))))))
|
(memorize-duplicated id-original id-duplicated)))))))
|
||||||
|
|
|
@ -592,15 +592,25 @@
|
||||||
|
|
||||||
(defn start-move-selected
|
(defn start-move-selected
|
||||||
"Enter mouse move mode, until mouse button is released."
|
"Enter mouse move mode, until mouse button is released."
|
||||||
[]
|
([]
|
||||||
|
(start-move-selected nil false))
|
||||||
|
|
||||||
|
([id shift?]
|
||||||
(ptk/reify ::start-move-selected
|
(ptk/reify ::start-move-selected
|
||||||
ptk/WatchEvent
|
ptk/WatchEvent
|
||||||
(watch [_ state stream]
|
(watch [_ state stream]
|
||||||
(let [initial (deref ms/mouse-position)
|
(let [initial (deref ms/mouse-position)
|
||||||
selected (wsh/lookup-selected state {:omit-blocked? true})
|
|
||||||
stopper (rx/filter ms/mouse-up? stream)
|
stopper (rx/filter ms/mouse-up? stream)
|
||||||
zoom (get-in state [:workspace-local :zoom] 1)]
|
zoom (get-in state [:workspace-local :zoom] 1)
|
||||||
(when-not (empty? selected)
|
|
||||||
|
;; 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
|
(->> ms/mouse-position
|
||||||
(rx/map #(gpt/to-vec initial %))
|
(rx/map #(gpt/to-vec initial %))
|
||||||
(rx/map #(gpt/length %))
|
(rx/map #(gpt/length %))
|
||||||
|
@ -609,14 +619,19 @@
|
||||||
(rx/with-latest vector ms/mouse-position-alt)
|
(rx/with-latest vector ms/mouse-position-alt)
|
||||||
(rx/mapcat
|
(rx/mapcat
|
||||||
(fn [[_ alt?]]
|
(fn [[_ alt?]]
|
||||||
|
(rx/concat
|
||||||
|
(if (some? id)
|
||||||
|
(rx/of (dws/select-shape id shift?))
|
||||||
|
(rx/empty))
|
||||||
|
|
||||||
(if alt?
|
(if alt?
|
||||||
;; When alt is down we start a duplicate+move
|
;; When alt is down we start a duplicate+move
|
||||||
(rx/of (start-move-duplicate initial)
|
(rx/of (start-move-duplicate initial)
|
||||||
(dws/duplicate-selected false))
|
(dws/duplicate-selected false))
|
||||||
;; Otherwise just plain old move
|
|
||||||
(rx/of (start-move initial selected)))))
|
|
||||||
(rx/take-until stopper)))))))
|
|
||||||
|
|
||||||
|
;; Otherwise just plain old move
|
||||||
|
(rx/of (start-move initial selected))))))
|
||||||
|
(rx/take-until stopper))))))))
|
||||||
(defn- start-move-duplicate
|
(defn- start-move-duplicate
|
||||||
[from-position]
|
[from-position]
|
||||||
(ptk/reify ::start-move-duplicate
|
(ptk/reify ::start-move-duplicate
|
||||||
|
|
|
@ -82,8 +82,8 @@
|
||||||
(cond
|
(cond
|
||||||
(d/num? new-value)
|
(d/num? new-value)
|
||||||
(-> new-value
|
(-> new-value
|
||||||
(cljs.core/max us/min-safe-int)
|
(cljs.core/max (/ us/min-safe-int 2))
|
||||||
(cljs.core/min us/max-safe-int)
|
(cljs.core/min (/ us/max-safe-int 2))
|
||||||
(cond->
|
(cond->
|
||||||
(d/num? min-val)
|
(d/num? min-val)
|
||||||
(cljs.core/max min-val)
|
(cljs.core/max min-val)
|
||||||
|
|
|
@ -33,10 +33,10 @@
|
||||||
(let [xf-get-bounds (comp (map #(get objects %)) (map #(calc-bounds % objects)))
|
(let [xf-get-bounds (comp (map #(get objects %)) (map #(calc-bounds % objects)))
|
||||||
padding (filters/calculate-padding object)
|
padding (filters/calculate-padding object)
|
||||||
obj-bounds (-> (filters/get-filters-bounds object)
|
obj-bounds (-> (filters/get-filters-bounds object)
|
||||||
(update :x - padding)
|
(update :x - (:horizontal padding))
|
||||||
(update :y - padding)
|
(update :y - (:vertical padding))
|
||||||
(update :width + (* 2 padding))
|
(update :width + (* 2 (:horizontal padding)))
|
||||||
(update :height + (* 2 padding)))]
|
(update :height + (* 2 (:vertical padding))))]
|
||||||
|
|
||||||
(cond
|
(cond
|
||||||
(and (= :group (:type object))
|
(and (= :group (:type object))
|
||||||
|
|
|
@ -175,12 +175,12 @@
|
||||||
(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
|
;; imported svgs can have fill and fill-opacity attributes
|
||||||
(obj/contains? svg-styles "fill")
|
(and (some? svg-styles) (obj/contains? svg-styles "fill"))
|
||||||
(-> styles
|
(-> styles
|
||||||
(obj/set! "fill" (obj/get svg-styles "fill"))
|
(obj/set! "fill" (obj/get svg-styles "fill"))
|
||||||
(obj/set! "fillOpacity" (obj/get svg-styles "fillOpacity")))
|
(obj/set! "fillOpacity" (obj/get svg-styles "fillOpacity")))
|
||||||
|
|
||||||
(obj/contains? svg-attrs "fill")
|
(and (some? svg-attrs) (obj/contains? svg-attrs "fill"))
|
||||||
(-> styles
|
(-> styles
|
||||||
(obj/set! "fill" (obj/get svg-attrs "fill"))
|
(obj/set! "fill" (obj/get svg-attrs "fill"))
|
||||||
(obj/set! "fillOpacity" (obj/get svg-attrs "fillOpacity")))
|
(obj/set! "fillOpacity" (obj/get svg-attrs "fillOpacity")))
|
||||||
|
|
|
@ -9,6 +9,7 @@
|
||||||
[app.common.data :as d]
|
[app.common.data :as d]
|
||||||
[app.common.data.macros :as dm]
|
[app.common.data.macros :as dm]
|
||||||
[app.common.geom.shapes :as gsh]
|
[app.common.geom.shapes :as gsh]
|
||||||
|
[app.common.pages.helpers :as cph]
|
||||||
[app.main.ui.context :as muc]
|
[app.main.ui.context :as muc]
|
||||||
[app.main.ui.shapes.attrs :as attrs]
|
[app.main.ui.shapes.attrs :as attrs]
|
||||||
[app.main.ui.shapes.gradients :as grad]
|
[app.main.ui.shapes.gradients :as grad]
|
||||||
|
@ -327,7 +328,11 @@
|
||||||
(obj/clone))
|
(obj/clone))
|
||||||
|
|
||||||
props (cond-> props
|
props (cond-> props
|
||||||
(d/not-empty? (:shadow shape))
|
(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)))
|
(obj/set! "filter" (dm/fmt "url(#filter_%)" render-id)))
|
||||||
|
|
||||||
svg-defs (:svg-defs shape {})
|
svg-defs (:svg-defs shape {})
|
||||||
|
@ -345,7 +350,7 @@
|
||||||
(obj/without ["fill" "fillOpacity"])))]
|
(obj/without ["fill" "fillOpacity"])))]
|
||||||
(obj/set! props "fill" (dm/fmt "url(#fill-0-%)" render-id)))
|
(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
|
(let [style
|
||||||
(-> (obj/get props "style")
|
(-> (obj/get props "style")
|
||||||
(obj/clone)
|
(obj/clone)
|
||||||
|
@ -354,7 +359,7 @@
|
||||||
(-> props
|
(-> props
|
||||||
(obj/set! "style" style)))
|
(obj/set! "style" style)))
|
||||||
|
|
||||||
(obj/contains? svg-attrs "fill")
|
(and (some? svg-attrs) (obj/contains? svg-attrs "fill"))
|
||||||
(let [style
|
(let [style
|
||||||
(-> (obj/get props "style")
|
(-> (obj/get props "style")
|
||||||
(obj/clone)
|
(obj/clone)
|
||||||
|
@ -374,10 +379,7 @@
|
||||||
|
|
||||||
(cond-> (obj/merge! props fill-props)
|
(cond-> (obj/merge! props fill-props)
|
||||||
(some? style)
|
(some? style)
|
||||||
(obj/set! "style" style)))
|
(obj/set! "style" style))))))
|
||||||
|
|
||||||
:else
|
|
||||||
props)))
|
|
||||||
|
|
||||||
(defn build-stroke-props [position child value render-id]
|
(defn build-stroke-props [position child value render-id]
|
||||||
(let [props (-> (obj/get child "props")
|
(let [props (-> (obj/get child "props")
|
||||||
|
@ -391,7 +393,19 @@
|
||||||
(obj/set! "fillOpacity" "none")))
|
(obj/set! "fillOpacity" "none")))
|
||||||
(add-style (obj/get (attrs/extract-stroke-attrs value position render-id) "style")))))
|
(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}
|
{::mf/wrap-props false}
|
||||||
[props]
|
[props]
|
||||||
(let [child (obj/get props "children")
|
(let [child (obj/get props "children")
|
||||||
|
@ -401,13 +415,9 @@
|
||||||
stroke-props (-> (obj/new)
|
stroke-props (-> (obj/new)
|
||||||
(obj/set! "id" (dm/fmt "strokes-%" (:id shape)))
|
(obj/set! "id" (dm/fmt "strokes-%" (:id shape)))
|
||||||
(cond->
|
(cond->
|
||||||
(some? (:blur shape))
|
(and (some? (:blur shape)) (not (cph/frame-shape? shape)))
|
||||||
(obj/set! "filter" (dm/fmt "url(#filter_blur_%)" render-id))))]
|
(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
|
(when
|
||||||
(d/not-empty? (:strokes shape))
|
(d/not-empty? (:strokes shape))
|
||||||
[:> :g stroke-props
|
[:> :g stroke-props
|
||||||
|
@ -416,3 +426,16 @@
|
||||||
shape (assoc value :points (:points shape))]
|
shape (assoc value :points (:points shape))]
|
||||||
[:& shape-custom-stroke {:shape shape :index index}
|
[:& shape-custom-stroke {:shape shape :index index}
|
||||||
[:> elem-name props]]))])]))
|
[:> 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]]))
|
||||||
|
|
|
@ -170,7 +170,6 @@
|
||||||
([shape filters blur-value]
|
([shape filters blur-value]
|
||||||
|
|
||||||
(let [svg-root? (and (= :svg-raw (:type shape)) (not= :svg (get-in shape [:content :tag])))
|
(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)]
|
{:keys [x y width height]} (:selrect shape)]
|
||||||
(if svg-root?
|
(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
|
;; 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)))
|
(map (partial filter-bounds shape)))
|
||||||
;; We add the selrect so the minimum size will be the selrect
|
;; We add the selrect so the minimum size will be the selrect
|
||||||
filter-bounds (conj filter-bounds (-> shape :points gsh/points->selrect))
|
filter-bounds (conj filter-bounds (-> shape :points gsh/points->selrect))
|
||||||
|
|
||||||
x1 (apply min (map :x1 filter-bounds))
|
x1 (apply min (map :x1 filter-bounds))
|
||||||
y1 (apply min (map :y1 filter-bounds))
|
y1 (apply min (map :y1 filter-bounds))
|
||||||
x2 (apply max (map :x2 filter-bounds))
|
x2 (apply max (map :x2 filter-bounds))
|
||||||
|
@ -195,8 +195,8 @@
|
||||||
|
|
||||||
;; We should move the frame filter coordinates because they should be
|
;; We should move the frame filter coordinates because they should be
|
||||||
;; relative with the frame. By default they come as absolute
|
;; relative with the frame. By default they come as absolute
|
||||||
{:x (if frame? (- x1 x) x1)
|
{:x x1
|
||||||
:y (if frame? (- y1 y) y1)
|
:y y1
|
||||||
:width (- x2 x1)
|
:width (- x2 x1)
|
||||||
:height (- y2 y1)})))))
|
:height (- y2 y1)})))))
|
||||||
|
|
||||||
|
@ -205,8 +205,20 @@
|
||||||
:center (/ (:stroke-width % 0) 2)
|
:center (/ (:stroke-width % 0) 2)
|
||||||
:outer (:stroke-width % 0)
|
:outer (:stroke-width % 0)
|
||||||
0) (:strokes shape)))
|
0) (:strokes shape)))
|
||||||
margin (apply max 0 (map #(gsh/shape-stroke-margin % stroke-width) (:strokes shape)))]
|
|
||||||
(+ stroke-width margin)))
|
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
|
(defn change-filter-in
|
||||||
"Adds the previous filter as `filter-in` parameter"
|
"Adds the previous filter as `filter-in` parameter"
|
||||||
|
@ -220,10 +232,10 @@
|
||||||
bounds (get-filters-bounds shape filters (or (-> shape :blur :value) 0))
|
bounds (get-filters-bounds shape filters (or (-> shape :blur :value) 0))
|
||||||
padding (calculate-padding shape)
|
padding (calculate-padding shape)
|
||||||
selrect (:selrect shape)
|
selrect (:selrect shape)
|
||||||
filter-x (/ (- (:x bounds) (:x selrect) padding) (:width selrect))
|
filter-x (/ (- (:x bounds) (:x selrect) (:horizontal padding)) (:width selrect))
|
||||||
filter-y (/ (- (:y bounds) (:y selrect) padding) (:height selrect))
|
filter-y (/ (- (:y bounds) (:y selrect) (:vertical padding)) (:height selrect))
|
||||||
filter-width (/ (+ (:width bounds) (* 2 padding)) (:width selrect))
|
filter-width (/ (+ (:width bounds) (* 2 (:horizontal padding))) (:width selrect))
|
||||||
filter-height (/ (+ (:height bounds) (* 2 padding)) (:height selrect))]
|
filter-height (/ (+ (:height bounds) (* 2 (:vertical padding))) (:height selrect))]
|
||||||
(when (> (count filters) 2)
|
(when (> (count filters) 2)
|
||||||
[:filter {:id filter-id
|
[:filter {:id filter-id
|
||||||
:x filter-x
|
:x filter-x
|
||||||
|
|
|
@ -7,9 +7,10 @@
|
||||||
(ns app.main.ui.shapes.frame
|
(ns app.main.ui.shapes.frame
|
||||||
(:require
|
(:require
|
||||||
[app.common.data.macros :as dm]
|
[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.attrs :as attrs]
|
||||||
[app.main.ui.shapes.custom-stroke :refer [shape-custom-strokes]]
|
[app.main.ui.shapes.custom-stroke :refer [shape-fills shape-strokes]]
|
||||||
[app.main.ui.shapes.filters :as filters]
|
|
||||||
[app.util.object :as obj]
|
[app.util.object :as obj]
|
||||||
[debug :refer [debug?]]
|
[debug :refer [debug?]]
|
||||||
[rumext.alpha :as mf]))
|
[rumext.alpha :as mf]))
|
||||||
|
@ -27,13 +28,12 @@
|
||||||
[{:keys [shape render-id]}]
|
[{:keys [shape render-id]}]
|
||||||
(when (= :frame (:type shape))
|
(when (= :frame (:type shape))
|
||||||
(let [{:keys [x y width height]} shape
|
(let [{:keys [x y width height]} shape
|
||||||
padding (filters/calculate-padding shape)
|
|
||||||
props (-> (attrs/extract-style-attrs shape)
|
props (-> (attrs/extract-style-attrs shape)
|
||||||
(obj/merge!
|
(obj/merge!
|
||||||
#js {:x (- x padding)
|
#js {:x x
|
||||||
:y (- y padding)
|
:y y
|
||||||
:width (+ width (* 2 padding))
|
:width width
|
||||||
:height (+ height (* 2 padding))}))
|
:height height}))
|
||||||
path? (some? (.-d props))]
|
path? (some? (.-d props))]
|
||||||
[:clipPath {:id (frame-clip-id shape render-id) :class "frame-clip"}
|
[:clipPath {:id (frame-clip-id shape render-id) :class "frame-clip"}
|
||||||
(if path?
|
(if path?
|
||||||
|
@ -63,22 +63,32 @@
|
||||||
(let [childs (unchecked-get props "childs")
|
(let [childs (unchecked-get props "childs")
|
||||||
shape (unchecked-get props "shape")
|
shape (unchecked-get props "shape")
|
||||||
{:keys [x y width height]} shape
|
{:keys [x y width height]} shape
|
||||||
|
transform (gsh/transform-matrix shape)
|
||||||
|
|
||||||
props (-> (attrs/extract-style-attrs shape)
|
props (-> (attrs/extract-style-attrs shape)
|
||||||
(obj/merge!
|
(obj/merge!
|
||||||
#js {:x x
|
#js {:x x
|
||||||
:y y
|
:y y
|
||||||
|
:transform transform
|
||||||
:width width
|
:width width
|
||||||
:height height
|
:height height
|
||||||
:className "frame-background"}))
|
:className "frame-background"}))
|
||||||
path? (some? (.-d props))]
|
path? (some? (.-d props))
|
||||||
|
render-id (mf/use-ctx muc/render-ctx)]
|
||||||
|
|
||||||
[:*
|
[:*
|
||||||
[:& shape-custom-strokes {:shape shape}
|
[:g {:clip-path (frame-clip-url shape render-id)}
|
||||||
|
[:*
|
||||||
|
[:& shape-fills {:shape shape}
|
||||||
(if path?
|
(if path?
|
||||||
[:> :path props]
|
[:> :path props]
|
||||||
[:> :rect props])]
|
[:> :rect props])]
|
||||||
|
|
||||||
(for [item childs]
|
(for [item childs]
|
||||||
[:& shape-wrapper {:shape item
|
[:& shape-wrapper {:shape item
|
||||||
:key (dm/str (:id item))}])])))
|
:key (dm/str (:id item))}])
|
||||||
|
[:& shape-strokes {:shape shape}
|
||||||
|
(if path?
|
||||||
|
[:> :path props]
|
||||||
|
[:> :rect props])]]]])))
|
||||||
|
|
||||||
|
|
|
@ -50,14 +50,11 @@
|
||||||
|
|
||||||
wrapper-props
|
wrapper-props
|
||||||
(cond-> 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)))
|
(obj/set! "filter" (filters/filter-str filter-id shape)))
|
||||||
|
|
||||||
wrapper-props
|
wrapper-props
|
||||||
(cond-> wrapper-props
|
(cond-> wrapper-props
|
||||||
(= :frame type)
|
|
||||||
(obj/set! "clipPath" (frame/frame-clip-url shape render-id))
|
|
||||||
|
|
||||||
(= :group type)
|
(= :group type)
|
||||||
(attrs/add-style-attrs shape render-id))]
|
(attrs/add-style-attrs shape render-id))]
|
||||||
|
|
||||||
|
|
|
@ -43,7 +43,8 @@
|
||||||
transform-mask? (and (= :mask tag)
|
transform-mask? (and (= :mask tag)
|
||||||
(= "userSpaceOnUse" (get attrs :maskUnits "objectBoundingBox")))
|
(= "userSpaceOnUse" (get attrs :maskUnits "objectBoundingBox")))
|
||||||
|
|
||||||
attrs (-> attrs
|
attrs
|
||||||
|
(-> attrs
|
||||||
(usvg/update-attr-ids prefix-id)
|
(usvg/update-attr-ids prefix-id)
|
||||||
(usvg/clean-attrs)
|
(usvg/clean-attrs)
|
||||||
;; This clasname will be used to change the transform on the viewport
|
;; This clasname will be used to change the transform on the viewport
|
||||||
|
@ -59,8 +60,7 @@
|
||||||
transform-gradient? (add-matrix :gradientTransform transform)
|
transform-gradient? (add-matrix :gradientTransform transform)
|
||||||
transform-pattern? (add-matrix :patternTransform transform)
|
transform-pattern? (add-matrix :patternTransform transform)
|
||||||
transform-clippath? (add-matrix :transform transform)
|
transform-clippath? (add-matrix :transform transform)
|
||||||
(or transform-filter?
|
(or transform-filter? transform-mask?) (merge bounds)))
|
||||||
transform-mask?) (merge attrs bounds)))
|
|
||||||
|
|
||||||
[wrapper wrapper-props] (if (= tag :mask)
|
[wrapper wrapper-props] (if (= tag :mask)
|
||||||
["g" #js {:className "svg-mask-wrapper"
|
["g" #js {:className "svg-mask-wrapper"
|
||||||
|
|
|
@ -7,6 +7,7 @@
|
||||||
(ns app.main.ui.shapes.text.fontfaces
|
(ns app.main.ui.shapes.text.fontfaces
|
||||||
(:require
|
(:require
|
||||||
[app.common.data :as d]
|
[app.common.data :as d]
|
||||||
|
[app.common.pages.helpers :as cph]
|
||||||
[app.main.fonts :as fonts]
|
[app.main.fonts :as fonts]
|
||||||
[app.main.ui.hooks :as hooks]
|
[app.main.ui.hooks :as hooks]
|
||||||
[app.main.ui.shapes.embed :as embed]
|
[app.main.ui.shapes.embed :as embed]
|
||||||
|
@ -76,14 +77,10 @@
|
||||||
{::mf/wrap-props false
|
{::mf/wrap-props false
|
||||||
::mf/wrap [#(mf/memo' % (mf/check-props ["shapes"]))]}
|
::mf/wrap [#(mf/memo' % (mf/check-props ["shapes"]))]}
|
||||||
[props]
|
[props]
|
||||||
(let [shapes (->> (obj/get props "shapes")
|
(let [;; Retrieve the fonts ids used by the text shapes
|
||||||
(filterv #(= :text (:type %))))
|
fonts (->> (obj/get props "shapes")
|
||||||
|
(filterv cph/text-shape?)
|
||||||
content (->> shapes (mapv :content))
|
(mapv (comp fonts/get-content-fonts :content))
|
||||||
|
|
||||||
;; Retrieve the fonts ids used by the text shapes
|
|
||||||
fonts (->> content
|
|
||||||
(mapv fonts/get-content-fonts)
|
|
||||||
(reduce set/union #{})
|
(reduce set/union #{})
|
||||||
(hooks/use-equal-memo))]
|
(hooks/use-equal-memo))]
|
||||||
|
|
||||||
|
|
|
@ -9,9 +9,12 @@
|
||||||
[app.common.data :as d]
|
[app.common.data :as d]
|
||||||
[app.common.exceptions :as ex]
|
[app.common.exceptions :as ex]
|
||||||
[app.common.geom.point :as gpt]
|
[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.comments :as dcm]
|
||||||
[app.main.data.viewer :as dv]
|
[app.main.data.viewer :as dv]
|
||||||
[app.main.data.viewer.shortcuts :as sc]
|
[app.main.data.viewer.shortcuts :as sc]
|
||||||
|
[app.main.fonts :as fonts]
|
||||||
[app.main.refs :as refs]
|
[app.main.refs :as refs]
|
||||||
[app.main.store :as st]
|
[app.main.store :as st]
|
||||||
[app.main.ui.hooks :as hooks]
|
[app.main.ui.hooks :as hooks]
|
||||||
|
@ -32,12 +35,17 @@
|
||||||
|
|
||||||
(defn- calculate-size
|
(defn- calculate-size
|
||||||
[frame zoom]
|
[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-width width
|
||||||
:base-height height
|
:base-height height
|
||||||
:width (* width zoom)
|
:width (* width zoom)
|
||||||
:height (* height zoom)
|
:height (* height zoom)
|
||||||
:vbox (str "0 0 " width " " height)}))
|
:vbox (str x " " y " " width " " height)}))
|
||||||
|
|
||||||
(defn- calculate-wrapper
|
(defn- calculate-wrapper
|
||||||
[size1 size2 zoom]
|
[size1 size2 zoom]
|
||||||
|
@ -70,6 +78,12 @@
|
||||||
(fn []
|
(fn []
|
||||||
(get-in data [:pages page-id])))
|
(get-in data [:pages page-id])))
|
||||||
|
|
||||||
|
text-shapes
|
||||||
|
(hooks/use-equal-memo
|
||||||
|
(->> (:objects page)
|
||||||
|
(vals)
|
||||||
|
(filter cph/text-shape?)))
|
||||||
|
|
||||||
zoom (:zoom local)
|
zoom (:zoom local)
|
||||||
frames (:frames page)
|
frames (:frames page)
|
||||||
frame (get frames index)
|
frame (get frames index)
|
||||||
|
@ -214,6 +228,13 @@
|
||||||
|
|
||||||
nil))))
|
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
|
[:div#viewer-layout {:class (dom/classnames
|
||||||
:force-visible (:show-thumbnails local)
|
:force-visible (:show-thumbnails local)
|
||||||
:viewer-layout (not= section :handoff)
|
:viewer-layout (not= section :handoff)
|
||||||
|
|
|
@ -21,7 +21,7 @@
|
||||||
|
|
||||||
(def type->options
|
(def type->options
|
||||||
{:multiple [:fill :stroke :image :text :shadow :blur]
|
{:multiple [:fill :stroke :image :text :shadow :blur]
|
||||||
:frame [:layout :fill :stroke]
|
:frame [:layout :fill :stroke :shadow :blur]
|
||||||
:group [:layout :svg]
|
:group [:layout :svg]
|
||||||
:rect [:layout :fill :stroke :shadow :blur :svg]
|
:rect [:layout :fill :stroke :shadow :blur :svg]
|
||||||
:circle [:layout :fill :stroke :shadow :blur :svg]
|
:circle [:layout :fill :stroke :shadow :blur :svg]
|
||||||
|
|
|
@ -13,6 +13,7 @@
|
||||||
[app.main.store :as st]
|
[app.main.store :as st]
|
||||||
[app.main.ui.shapes.bool :as bool]
|
[app.main.ui.shapes.bool :as bool]
|
||||||
[app.main.ui.shapes.circle :as circle]
|
[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.frame :as frame]
|
||||||
[app.main.ui.shapes.group :as group]
|
[app.main.ui.shapes.group :as group]
|
||||||
[app.main.ui.shapes.image :as image]
|
[app.main.ui.shapes.image :as image]
|
||||||
|
@ -190,9 +191,18 @@
|
||||||
frame (get objects (:id frame))
|
frame (get objects (:id frame))
|
||||||
|
|
||||||
zoom (:zoom local 1)
|
zoom (:zoom local 1)
|
||||||
width (* (:width frame) zoom)
|
|
||||||
height (* (:height frame) zoom)
|
{:keys [_ _ width height]} (filters/get-filters-bounds frame)
|
||||||
vbox (str "0 0 " (:width frame 0) " " (:height frame 0))
|
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
|
render (mf/use-memo
|
||||||
(mf/deps objects)
|
(mf/deps objects)
|
||||||
|
|
|
@ -403,9 +403,9 @@
|
||||||
modifier-ids (into [frame-id] (cph/get-children-ids objects frame-id))
|
modifier-ids (into [frame-id] (cph/get-children-ids objects frame-id))
|
||||||
objects (reduce update-fn objects modifier-ids)
|
objects (reduce update-fn objects modifier-ids)
|
||||||
frame (assoc-in frame [:modifiers :displacement] modifier)
|
frame (assoc-in frame [:modifiers :displacement] modifier)
|
||||||
|
|
||||||
width (* (:width frame) zoom)
|
width (* (:width frame) zoom)
|
||||||
height (* (:height frame) zoom)
|
height (* (:height frame) zoom)
|
||||||
|
|
||||||
vbox (str "0 0 " (:width frame 0)
|
vbox (str "0 0 " (:width frame 0)
|
||||||
" " (:height frame 0))
|
" " (:height frame 0))
|
||||||
wrapper (mf/use-memo
|
wrapper (mf/use-memo
|
||||||
|
|
|
@ -36,7 +36,7 @@
|
||||||
|
|
||||||
;; NOTE: this is necessary because the `cph/get-component`
|
;; NOTE: this is necessary because the `cph/get-component`
|
||||||
;; expects a map of all libraries, including the local one.
|
;; 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)
|
component (cph/get-component libraries library-id component-id)
|
||||||
show? (some? component-id)
|
show? (some? component-id)
|
||||||
|
|
|
@ -88,6 +88,7 @@
|
||||||
|
|
||||||
;; REFS
|
;; REFS
|
||||||
viewport-ref (mf/use-ref nil)
|
viewport-ref (mf/use-ref nil)
|
||||||
|
overlays-ref (mf/use-ref nil)
|
||||||
|
|
||||||
;; VARS
|
;; VARS
|
||||||
disable-paste (mf/use-var false)
|
disable-paste (mf/use-var false)
|
||||||
|
@ -121,7 +122,7 @@
|
||||||
node-editing? (and edition (not= :text (get-in base-objects [edition :type])))
|
node-editing? (and edition (not= :text (get-in base-objects [edition :type])))
|
||||||
text-editing? (and edition (= :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-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-double-click (actions/on-double-click hover hover-ids drawing-path? base-objects edition)
|
||||||
on-drag-enter (actions/on-drag-enter)
|
on-drag-enter (actions/on-drag-enter)
|
||||||
|
@ -169,7 +170,7 @@
|
||||||
|
|
||||||
disabled-guides? (or drawing-tool transform)]
|
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-viewport-size viewport-ref)
|
||||||
(hooks/setup-cursor cursor alt? mod? space? panning drawing-tool drawing-path? node-editing?)
|
(hooks/setup-cursor cursor alt? mod? space? panning drawing-tool drawing-path? node-editing?)
|
||||||
(hooks/setup-keyboard alt? mod? space?)
|
(hooks/setup-keyboard alt? mod? space?)
|
||||||
|
@ -179,7 +180,7 @@
|
||||||
(hooks/setup-active-frames base-objects vbox hover active-frames)
|
(hooks/setup-active-frames base-objects vbox hover active-frames)
|
||||||
|
|
||||||
[:div.viewport
|
[:div.viewport
|
||||||
[:div.viewport-overlays
|
[:div.viewport-overlays {:ref overlays-ref}
|
||||||
|
|
||||||
[:& wtr/frame-renderer {:objects base-objects
|
[:& wtr/frame-renderer {:objects base-objects
|
||||||
:background background}]
|
:background background}]
|
||||||
|
|
|
@ -97,9 +97,7 @@
|
||||||
(st/emit! (dw/handle-area-selection shift? mod?))
|
(st/emit! (dw/handle-area-selection shift? mod?))
|
||||||
|
|
||||||
(not drawing-tool)
|
(not drawing-tool)
|
||||||
(st/emit! (when (or shift? (not selected?))
|
(st/emit! (dw/start-move-selected id shift?)))))))))))
|
||||||
(dw/select-shape id shift?))
|
|
||||||
(dw/start-move-selected)))))))))))
|
|
||||||
|
|
||||||
(defn on-move-selected
|
(defn on-move-selected
|
||||||
[hover hover-ids selected space?]
|
[hover hover-ids selected space?]
|
||||||
|
@ -147,27 +145,25 @@
|
||||||
(reset! frame-hover nil))))
|
(reset! frame-hover nil))))
|
||||||
|
|
||||||
(defn on-click
|
(defn on-click
|
||||||
[hover selected edition drawing-path? drawing-tool space?]
|
[hover selected edition drawing-path? drawing-tool space? selrect]
|
||||||
(mf/use-callback
|
(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]
|
(fn [event]
|
||||||
(when (or (dom/class? (dom/get-target event) "viewport-controls")
|
(when (and (nil? selrect)
|
||||||
(dom/class? (dom/get-target event) "viewport-selrect"))
|
(or (dom/class? (dom/get-target event) "viewport-controls")
|
||||||
|
(dom/class? (dom/get-target event) "viewport-selrect")))
|
||||||
(let [ctrl? (kbd/ctrl? event)
|
(let [ctrl? (kbd/ctrl? event)
|
||||||
shift? (kbd/shift? event)
|
shift? (kbd/shift? event)
|
||||||
alt? (kbd/alt? event)
|
alt? (kbd/alt? event)
|
||||||
meta? (kbd/meta? event)
|
meta? (kbd/meta? event)
|
||||||
mod? (kbd/mod? event)
|
mod? (kbd/mod? event)
|
||||||
|
|
||||||
hovering? (some? @hover)
|
hovering? (some? @hover)
|
||||||
frame? (= :frame (:type @hover))
|
frame? (= :frame (:type @hover))]
|
||||||
selected? (contains? selected (:id @hover))]
|
|
||||||
(st/emit! (ms/->MouseEvent :click ctrl? shift? alt? meta?))
|
(st/emit! (ms/->MouseEvent :click ctrl? shift? alt? meta?))
|
||||||
|
|
||||||
(when (and hovering?
|
(when (and hovering?
|
||||||
(or (not frame?) mod?)
|
(or (not frame?) mod?)
|
||||||
(not @space?)
|
(not @space?)
|
||||||
(not selected?)
|
|
||||||
(not edition)
|
(not edition)
|
||||||
(not drawing-path?)
|
(not drawing-path?)
|
||||||
(not drawing-tool))
|
(not drawing-tool))
|
||||||
|
@ -367,21 +363,23 @@
|
||||||
pt (utils/translate-point-to-viewport viewport zoom raw-pt)]
|
pt (utils/translate-point-to-viewport viewport zoom raw-pt)]
|
||||||
(rx/push! move-stream 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/use-callback
|
||||||
(mf/deps zoom)
|
(mf/deps zoom)
|
||||||
(fn [event]
|
(fn [event]
|
||||||
(let [viewport (mf/ref-val viewport-ref)
|
(let [viewport (mf/ref-val viewport-ref)
|
||||||
|
overlays (mf/ref-val overlays-ref)
|
||||||
event (.getBrowserEvent ^js event)
|
event (.getBrowserEvent ^js event)
|
||||||
target (dom/get-target event)]
|
target (dom/get-target event)
|
||||||
(when (.contains ^js viewport target)
|
mod? (kbd/mod? event)]
|
||||||
|
|
||||||
|
(when (or (dom/is-child? viewport target)
|
||||||
|
(dom/is-child? overlays target))
|
||||||
(dom/prevent-default event)
|
(dom/prevent-default event)
|
||||||
(dom/stop-propagation event)
|
(dom/stop-propagation event)
|
||||||
(let [pt (->> (dom/get-client-position event)
|
(let [pt (->> (dom/get-client-position event)
|
||||||
(utils/translate-point-to-viewport viewport zoom))
|
(utils/translate-point-to-viewport viewport zoom))
|
||||||
|
|
||||||
mod? (kbd/mod? event)
|
|
||||||
|
|
||||||
delta-mode (.-deltaMode ^js event)
|
delta-mode (.-deltaMode ^js event)
|
||||||
|
|
||||||
unit (cond
|
unit (cond
|
||||||
|
|
|
@ -27,11 +27,11 @@
|
||||||
[rumext.alpha :as mf])
|
[rumext.alpha :as mf])
|
||||||
(:import goog.events.EventType))
|
(: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)
|
(let [on-key-down (actions/on-key-down)
|
||||||
on-key-up (actions/on-key-up)
|
on-key-up (actions/on-key-up)
|
||||||
on-mouse-move (actions/on-mouse-move viewport-ref zoom)
|
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?)]
|
on-paste (actions/on-paste disable-paste in-viewport?)]
|
||||||
(mf/use-layout-effect
|
(mf/use-layout-effect
|
||||||
(mf/deps on-key-down on-key-up on-mouse-move on-mouse-wheel on-paste)
|
(mf/deps on-key-down on-key-up on-mouse-move on-mouse-wheel on-paste)
|
||||||
|
|
|
@ -530,3 +530,8 @@
|
||||||
(when onfinish
|
(when onfinish
|
||||||
(set! (.-onfinish animation) onfinish)))))
|
(set! (.-onfinish animation) onfinish)))))
|
||||||
|
|
||||||
|
(defn is-child?
|
||||||
|
[^js node ^js candidate]
|
||||||
|
(and (some? node)
|
||||||
|
(some? candidate)
|
||||||
|
(.contains node candidate)))
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue