mirror of
https://github.com/penpot/penpot.git
synced 2025-04-12 15:01:28 -05:00
Merge remote-tracking branch 'origin/staging' into develop
This commit is contained in:
commit
e9ae59ad00
67 changed files with 1278 additions and 1087 deletions
11
CHANGES.md
11
CHANGES.md
|
@ -28,6 +28,17 @@
|
|||
- Fix problem with pan and space [#811](https://github.com/penpot/penpot/issues/811)
|
||||
- Fix issue when parsing exponential numbers in paths
|
||||
- Remove legacy system user and team [#843](https://github.com/penpot/penpot/issues/843)
|
||||
- Fix ordering of copy pasted objects [Taiga #1618](https://tree.taiga.io/project/penpot/issue/1617)
|
||||
- Fix problems with blending modes [#837](https://github.com/penpot/penpot/issues/837)
|
||||
- Fix problem with zoom an selection rect [#845](https://github.com/penpot/penpot/issues/845)
|
||||
- Fix problem displaying team statistics [#859](https://github.com/penpot/penpot/issues/859)
|
||||
- Fix problems with text editor selection [Taiga #1546](https://tree.taiga.io/project/penpot/issue/1546)
|
||||
- Fix problem when opening the context menu in dashboard at the bottom [#856](https://github.com/penpot/penpot/issues/856)
|
||||
- Fix problem when clicking an interactive group in view mode [#863](https://github.com/penpot/penpot/issues/863)
|
||||
- Fix visibility of pages in sitemap when changing page [Taiga #1618](https://tree.taiga.io/project/penpot/issue/1618)
|
||||
- Fix visual problem with group invite [Taiga #1290](https://tree.taiga.io/project/penpot/issue/1290)
|
||||
- Fix issues with promote owner panel [Taiga #763](https://tree.taiga.io/project/penpot/issue/763)
|
||||
- Allow use library colors when defining gradients [Taiga #1614](https://tree.taiga.io/project/penpot/issue/1614)
|
||||
|
||||
### :arrow_up: Deps updates
|
||||
|
||||
|
|
|
@ -17,28 +17,6 @@
|
|||
[app.common.geom.shapes.intersect :as gin]
|
||||
[app.common.spec :as us]))
|
||||
|
||||
;; --- Relative Movement
|
||||
|
||||
(defn move
|
||||
"Move the shape relativelly to its current
|
||||
position applying the provided delta."
|
||||
[shape {dx :x dy :y}]
|
||||
(let [dx (d/check-num dx)
|
||||
dy (d/check-num dy)]
|
||||
(-> shape
|
||||
(assoc-in [:modifiers :displacement] (gmt/translate-matrix (gpt/point dx dy)))
|
||||
(gtr/transform-shape))))
|
||||
|
||||
;; --- Absolute Movement
|
||||
|
||||
(declare absolute-move-rect)
|
||||
|
||||
(defn absolute-move
|
||||
"Move the shape to the exactly specified position."
|
||||
[shape {:keys [x y]}]
|
||||
(let [dx (- (d/check-num x) (-> shape :selrect :x))
|
||||
dy (- (d/check-num y) (-> shape :selrect :y))]
|
||||
(move shape (gpt/point dx dy))))
|
||||
|
||||
;; --- Resize (Dimensions)
|
||||
(defn resize-modifiers
|
||||
|
@ -120,38 +98,8 @@
|
|||
(gpr/join-selrects)))
|
||||
|
||||
(defn translate-to-frame
|
||||
[{:keys [type x y] :as shape} {:keys [x y]}]
|
||||
(let [move-point
|
||||
(fn [point]
|
||||
(-> point
|
||||
(update :x - x)
|
||||
(update :y - y)))
|
||||
|
||||
move-segment
|
||||
(fn [segment]
|
||||
(-> segment
|
||||
(d/update-in-when [:params :x] - x)
|
||||
(d/update-in-when [:params :y] - y)
|
||||
(d/update-in-when [:params :c1x] - x)
|
||||
(d/update-in-when [:params :c1y] - y)
|
||||
(d/update-in-when [:params :c2x] - x)
|
||||
(d/update-in-when [:params :c2y] - y)))]
|
||||
|
||||
(-> shape
|
||||
(d/update-when :x - x)
|
||||
(d/update-when :y - y)
|
||||
(update-in [:selrect :x] - x)
|
||||
(update-in [:selrect :y] - y)
|
||||
(update-in [:selrect :x1] - x)
|
||||
(update-in [:selrect :y1] - y)
|
||||
(update-in [:selrect :x2] - x)
|
||||
(update-in [:selrect :y2] - y)
|
||||
|
||||
(d/update-when :points #(mapv move-point %))
|
||||
|
||||
(cond-> (= :path type)
|
||||
(d/update-when :content #(mapv move-segment %))))))
|
||||
|
||||
[shape {:keys [x y]}]
|
||||
(gtr/move shape (gpt/negate (gpt/point x y))) )
|
||||
|
||||
;; --- Helpers
|
||||
|
||||
|
@ -244,6 +192,8 @@
|
|||
(d/export gtr/update-group-selrect)
|
||||
(d/export gtr/transform-points)
|
||||
(d/export gtr/calculate-adjust-matrix)
|
||||
(d/export gtr/move)
|
||||
(d/export gtr/absolute-move)
|
||||
|
||||
;; PATHS
|
||||
(d/export gsp/content->points)
|
||||
|
|
|
@ -139,6 +139,23 @@
|
|||
(update :width #(if (mth/almost-zero? %) 1 %))
|
||||
(update :height #(if (mth/almost-zero? %) 1 %)))))
|
||||
|
||||
(defn move-content [content move-vec]
|
||||
(let [set-tr (fn [params px py]
|
||||
(let [tr-point (-> (gpt/point (get params px) (get params py))
|
||||
(gpt/add move-vec))]
|
||||
(assoc params
|
||||
px (:x tr-point)
|
||||
py (:y tr-point))))
|
||||
|
||||
transform-params
|
||||
(fn [{:keys [x c1x c2x] :as params}]
|
||||
(cond-> params
|
||||
(not (nil? x)) (set-tr :x :y)
|
||||
(not (nil? c1x)) (set-tr :c1x :c1y)
|
||||
(not (nil? c2x)) (set-tr :c2x :c2y)))]
|
||||
|
||||
(mapv #(update % :params transform-params) content)))
|
||||
|
||||
(defn transform-content [content transform]
|
||||
(let [set-tr (fn [params px py]
|
||||
(let [tr-point (-> (gpt/point (get params px) (get params py))
|
||||
|
|
|
@ -14,6 +14,49 @@
|
|||
[app.common.math :as mth]
|
||||
[app.common.data :as d]))
|
||||
|
||||
;; --- Relative Movement
|
||||
|
||||
(defn move-selrect [selrect {dx :x dy :y}]
|
||||
(-> selrect
|
||||
(d/update-when :x + dx)
|
||||
(d/update-when :y + dy)
|
||||
(d/update-when :x1 + dx)
|
||||
(d/update-when :y1 + dy)
|
||||
(d/update-when :x2 + dx)
|
||||
(d/update-when :y2 + dy)))
|
||||
|
||||
(defn move-points [points move-vec]
|
||||
(->> points
|
||||
(mapv #(gpt/add % move-vec))))
|
||||
|
||||
(defn move
|
||||
"Move the shape relativelly to its current
|
||||
position applying the provided delta."
|
||||
[shape {dx :x dy :y}]
|
||||
(let [dx (d/check-num dx)
|
||||
dy (d/check-num dy)
|
||||
move-vec (gpt/point dx dy)]
|
||||
|
||||
(-> shape
|
||||
(update :selrect move-selrect move-vec)
|
||||
(update :points move-points move-vec)
|
||||
(d/update-when :x + dx)
|
||||
(d/update-when :y + dy)
|
||||
(cond-> (= :path (:type shape))
|
||||
(update :content gpa/move-content move-vec)))))
|
||||
|
||||
;; --- Absolute Movement
|
||||
|
||||
(declare absolute-move-rect)
|
||||
|
||||
(defn absolute-move
|
||||
"Move the shape to the exactly specified position."
|
||||
[shape {:keys [x y]}]
|
||||
(let [dx (- (d/check-num x) (-> shape :selrect :x))
|
||||
dy (- (d/check-num y) (-> shape :selrect :y))]
|
||||
(move shape (gpt/point dx dy))))
|
||||
|
||||
|
||||
(defn- modif-rotation [shape]
|
||||
(let [cur-rotation (d/check-num (:rotation shape))
|
||||
delta-angle (d/check-num (get-in shape [:modifiers :rotation]))]
|
||||
|
@ -272,12 +315,27 @@
|
|||
(and rx (< rx 0)) (update :flip-x not)
|
||||
(and ry (< ry 0)) (update :flip-y not))))
|
||||
|
||||
(defn transform-shape [shape]
|
||||
(let [center (gco/center-shape shape)]
|
||||
(if (and (:modifiers shape) center)
|
||||
(let [transform (modifiers->transform center (:modifiers shape))]
|
||||
(defn apply-displacement [shape]
|
||||
(let [modifiers (:modifiers shape)]
|
||||
(if (contains? modifiers :displacement)
|
||||
(let [mov-vec (-> (gpt/point 0 0)
|
||||
(gpt/transform (:displacement modifiers)))
|
||||
shape (move shape mov-vec)
|
||||
modifiers (dissoc modifiers :displacement)]
|
||||
(-> shape
|
||||
(set-flip (:modifiers shape))
|
||||
(assoc :modifiers modifiers)
|
||||
(cond-> (empty? modifiers)
|
||||
(dissoc :modifiers))))
|
||||
shape)))
|
||||
|
||||
(defn transform-shape [shape]
|
||||
(let [shape (apply-displacement shape)
|
||||
center (gco/center-shape shape)
|
||||
modifiers (:modifiers shape)]
|
||||
(if (and modifiers center)
|
||||
(let [transform (modifiers->transform center modifiers)]
|
||||
(-> shape
|
||||
(set-flip modifiers)
|
||||
(apply-transform transform)
|
||||
(dissoc :modifiers)))
|
||||
shape)))
|
||||
|
|
|
@ -31,7 +31,7 @@
|
|||
;; When verify? false we spec the schema validation. Currently used to make just
|
||||
;; 1 validation even if the changes are applied twice
|
||||
(when verify?
|
||||
(us/verify ::spec/changes items))
|
||||
(us/assert ::spec/changes items))
|
||||
|
||||
(let [pages (into #{} (map :page-id) items)
|
||||
result (->> items
|
||||
|
|
|
@ -24,7 +24,8 @@
|
|||
}
|
||||
|
||||
.custom-select {
|
||||
width: 103px
|
||||
width: 103px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.action-buttons {
|
||||
|
|
|
@ -134,6 +134,11 @@
|
|||
margin-bottom: 0px;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
.btn-disabled {
|
||||
opacity: 0.5;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -7,7 +7,6 @@
|
|||
(ns app.main.data.workspace
|
||||
(:require
|
||||
[app.common.data :as d]
|
||||
[app.common.exceptions :as ex]
|
||||
[app.common.geom.align :as gal]
|
||||
[app.common.geom.matrix :as gmt]
|
||||
[app.common.geom.point :as gpt]
|
||||
|
@ -19,37 +18,31 @@
|
|||
[app.common.spec :as us]
|
||||
[app.common.uuid :as uuid]
|
||||
[app.config :as cfg]
|
||||
[app.main.constants :as c]
|
||||
[app.main.data.workspace.colors :as mdc]
|
||||
[app.main.data.messages :as dm]
|
||||
[app.main.data.workspace.changes :as dch]
|
||||
[app.main.data.workspace.common :as dwc]
|
||||
[app.main.data.workspace.drawing :as dwd]
|
||||
[app.main.data.workspace.path :as dwdp]
|
||||
[app.main.data.workspace.groups :as dwg]
|
||||
[app.main.data.workspace.libraries :as dwl]
|
||||
[app.main.data.workspace.notifications :as dwn]
|
||||
[app.main.data.workspace.path :as dwdp]
|
||||
[app.main.data.workspace.persistence :as dwp]
|
||||
[app.main.data.workspace.selection :as dws]
|
||||
[app.main.data.workspace.transforms :as dwt]
|
||||
[app.main.data.workspace.svg-upload :as svg]
|
||||
[app.main.data.workspace.transforms :as dwt]
|
||||
[app.main.data.workspace.undo :as dwu]
|
||||
[app.main.repo :as rp]
|
||||
[app.main.store :as st]
|
||||
[app.main.streams :as ms]
|
||||
[app.main.worker :as uw]
|
||||
[app.util.dom :as dom]
|
||||
[app.util.http :as http]
|
||||
[app.util.i18n :refer [tr] :as i18n]
|
||||
[app.util.i18n :as i18n]
|
||||
[app.util.logging :as log]
|
||||
[app.util.object :as obj]
|
||||
[app.util.router :as rt]
|
||||
[app.util.timers :as ts]
|
||||
[app.util.transit :as t]
|
||||
[app.util.webapi :as wapi]
|
||||
[beicon.core :as rx]
|
||||
[cljs.spec.alpha :as s]
|
||||
[clojure.set :as set]
|
||||
[cuerdas.core :as str]
|
||||
[goog.string.path :as path]
|
||||
[potok.core :as ptk]))
|
||||
|
||||
;; (log/set-level! :trace)
|
||||
|
@ -269,7 +262,7 @@
|
|||
:name name}
|
||||
uchange {:type :del-page
|
||||
:id id}]
|
||||
(rx/of (dwc/commit-changes [rchange] [uchange] {:commit-local? true}))))))
|
||||
(rx/of (dch/commit-changes [rchange] [uchange] {:commit-local? true}))))))
|
||||
|
||||
(defn duplicate-page [page-id]
|
||||
(ptk/reify ::duplicate-page
|
||||
|
@ -287,7 +280,7 @@
|
|||
:page page}
|
||||
uchange {:type :del-page
|
||||
:id id}]
|
||||
(rx/of (dwc/commit-changes [rchange] [uchange] {:commit-local? true}))))))
|
||||
(rx/of (dch/commit-changes [rchange] [uchange] {:commit-local? true}))))))
|
||||
|
||||
(s/def ::rename-page
|
||||
(s/keys :req-un [::id ::name]))
|
||||
|
@ -306,7 +299,7 @@
|
|||
uchg {:type :mod-page
|
||||
:id id
|
||||
:name (:name page)}]
|
||||
(rx/of (dwc/commit-changes [rchg] [uchg] {:commit-local? true}))))))
|
||||
(rx/of (dch/commit-changes [rchg] [uchg] {:commit-local? true}))))))
|
||||
|
||||
(declare purge-page)
|
||||
(declare go-to-file)
|
||||
|
@ -323,7 +316,7 @@
|
|||
:id id}
|
||||
uchg {:type :add-page
|
||||
:page page}]
|
||||
(rx/of (dwc/commit-changes [rchg] [uchg] {:commit-local? true})
|
||||
(rx/of (dch/commit-changes [rchg] [uchg] {:commit-local? true})
|
||||
(when (= id (:current-page-id state))
|
||||
go-to-file))))))
|
||||
|
||||
|
@ -604,7 +597,7 @@
|
|||
(ptk/reify ::update-shape
|
||||
ptk/WatchEvent
|
||||
(watch [_ state stream]
|
||||
(rx/of (dwc/update-shapes [id] #(merge % attrs))))))
|
||||
(rx/of (dch/update-shapes [id] #(merge % attrs))))))
|
||||
|
||||
(defn start-rename-shape
|
||||
[id]
|
||||
|
@ -712,7 +705,7 @@
|
|||
:index (cp/position-on-parent id objects)}))
|
||||
selected)]
|
||||
;; TODO: maybe missing the :reg-objects event?
|
||||
(rx/of (dwc/commit-changes rchanges uchanges {:commit-local? true}))))))
|
||||
(rx/of (dch/commit-changes rchanges uchanges {:commit-local? true}))))))
|
||||
|
||||
|
||||
;; --- Change Shape Order (D&D Ordering)
|
||||
|
@ -986,7 +979,7 @@
|
|||
shapes-to-detach
|
||||
shapes-to-reroot
|
||||
shapes-to-deroot)]
|
||||
(rx/of (dwc/commit-changes rchanges uchanges {:commit-local? true})
|
||||
(rx/of (dch/commit-changes rchanges uchanges {:commit-local? true})
|
||||
(dwc/expand-collapse parent-id))))))
|
||||
|
||||
(defn relocate-selected-shapes
|
||||
|
@ -1039,7 +1032,7 @@
|
|||
uchg {:type :mov-page
|
||||
:id id
|
||||
:index cidx}]
|
||||
(rx/of (dwc/commit-changes [rchg] [uchg] {:commit-local? true}))))))
|
||||
(rx/of (dch/commit-changes [rchg] [uchg] {:commit-local? true}))))))
|
||||
|
||||
;; --- Shape / Selection Alignment and Distribution
|
||||
|
||||
|
@ -1057,30 +1050,13 @@
|
|||
selected (get-in state [:workspace-local :selected])
|
||||
moved (if (= 1 (count selected))
|
||||
(align-object-to-frame objects (first selected) axis)
|
||||
(align-objects-list objects selected axis))]
|
||||
(loop [moved (seq moved)
|
||||
rchanges []
|
||||
uchanges []]
|
||||
(if (nil? moved)
|
||||
(do
|
||||
;; (println "================ rchanges")
|
||||
;; (cljs.pprint/pprint rchanges)
|
||||
;; (println "================ uchanges")
|
||||
;; (cljs.pprint/pprint uchanges)
|
||||
(rx/of (dwc/commit-changes rchanges uchanges {:commit-local? true})))
|
||||
(let [curr (first moved)
|
||||
prev (get objects (:id curr))
|
||||
ops1 (dwc/generate-operations prev curr)
|
||||
ops2 (dwc/generate-operations curr prev true)]
|
||||
(recur (next moved)
|
||||
(conj rchanges {:type :mod-obj
|
||||
:page-id page-id
|
||||
:operations ops1
|
||||
:id (:id curr)})
|
||||
(conj uchanges {:type :mod-obj
|
||||
:page-id page-id
|
||||
:operations ops2
|
||||
:id (:id curr)})))))))))
|
||||
(align-objects-list objects selected axis))
|
||||
|
||||
moved-objects (->> moved (group-by :id))
|
||||
ids (keys moved-objects)
|
||||
update-fn (fn [shape] (first (get moved-objects (:id shape))))]
|
||||
|
||||
(rx/of (dch/update-shapes ids update-fn {:reg-objects? true}))))))
|
||||
|
||||
(defn align-object-to-frame
|
||||
[objects object-id axis]
|
||||
|
@ -1104,30 +1080,12 @@
|
|||
objects (dwc/lookup-page-objects state page-id)
|
||||
selected (get-in state [:workspace-local :selected])
|
||||
moved (-> (map #(get objects %) selected)
|
||||
(gal/distribute-space axis objects))]
|
||||
(loop [moved (seq moved)
|
||||
rchanges []
|
||||
uchanges []]
|
||||
(if (nil? moved)
|
||||
(do
|
||||
;; (println "================ rchanges")
|
||||
;; (cljs.pprint/pprint rchanges)
|
||||
;; (println "================ uchanges")
|
||||
;; (cljs.pprint/pprint uchanges)
|
||||
(rx/of (dwc/commit-changes rchanges uchanges {:commit-local? true})))
|
||||
(let [curr (first moved)
|
||||
prev (get objects (:id curr))
|
||||
ops1 (dwc/generate-operations prev curr)
|
||||
ops2 (dwc/generate-operations curr prev true)]
|
||||
(recur (next moved)
|
||||
(conj rchanges {:type :mod-obj
|
||||
:page-id page-id
|
||||
:operations ops1
|
||||
:id (:id curr)})
|
||||
(conj uchanges {:type :mod-obj
|
||||
:page-id page-id
|
||||
:operations ops2
|
||||
:id (:id curr)})))))))))
|
||||
(gal/distribute-space axis objects))
|
||||
|
||||
moved-objects (->> moved (group-by :id))
|
||||
ids (keys moved-objects)
|
||||
update-fn (fn [shape] (first (get moved-objects (:id shape))))]
|
||||
(rx/of (dch/update-shapes ids update-fn {:reg-objects? true}))))))
|
||||
|
||||
;; --- Shape Proportions
|
||||
|
||||
|
@ -1141,7 +1099,7 @@
|
|||
(assoc shape :proportion-lock false)
|
||||
(-> (assoc shape :proportion-lock true)
|
||||
(gpr/assign-proportions))))]
|
||||
(rx/of (dwc/update-shapes [id] assign-proportions))))))
|
||||
(rx/of (dch/update-shapes [id] assign-proportions))))))
|
||||
|
||||
;; --- Update Shape Position
|
||||
|
||||
|
@ -1183,7 +1141,7 @@
|
|||
(cond-> obj
|
||||
(boolean? blocked) (assoc :blocked blocked)
|
||||
(boolean? hidden) (assoc :hidden hidden)))]
|
||||
(rx/of (dwc/update-shapes-recursive [id] update-fn))))))
|
||||
(rx/of (dch/update-shapes-recursive [id] update-fn))))))
|
||||
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
@ -1410,7 +1368,7 @@
|
|||
(catch :default e
|
||||
(let [data (ex-data e)]
|
||||
(if (:not-implemented data)
|
||||
(rx/of (dm/warn (tr "errors.clipboard-not-implemented")))
|
||||
(rx/of (dm/warn (i18n/tr "errors.clipboard-not-implemented")))
|
||||
(js/console.error "ERROR" e))))))))
|
||||
|
||||
(defn paste-from-event
|
||||
|
@ -1582,7 +1540,7 @@
|
|||
(map #(get-in % [:obj :id]))
|
||||
(into (d/ordered-set)))]
|
||||
|
||||
(rx/of (dwc/commit-changes rchanges uchanges {:commit-local? true})
|
||||
(rx/of (dch/commit-changes rchanges uchanges {:commit-local? true})
|
||||
(dwc/select-shapes selected))))]
|
||||
(ptk/reify ::paste-shape
|
||||
ptk/WatchEvent
|
||||
|
@ -1629,10 +1587,10 @@
|
|||
:height height
|
||||
:grow-type (if (> (count text) 100) :auto-height :auto-width)
|
||||
:content (as-content text)})]
|
||||
(rx/of (dwc/start-undo-transaction)
|
||||
(rx/of (dwu/start-undo-transaction)
|
||||
(dws/deselect-all)
|
||||
(dwc/add-shape shape)
|
||||
(dwc/commit-undo-transaction))))))
|
||||
(dwu/commit-undo-transaction))))))
|
||||
|
||||
(defn- paste-svg
|
||||
[text]
|
||||
|
@ -1743,7 +1701,7 @@
|
|||
(let [page-id (get state :current-page-id)
|
||||
options (dwc/lookup-page-options state page-id)
|
||||
previus-color (:background options)]
|
||||
(rx/of (dwc/commit-changes
|
||||
(rx/of (dch/commit-changes
|
||||
[{:type :set-option
|
||||
:page-id page-id
|
||||
:option :background
|
||||
|
|
222
frontend/src/app/main/data/workspace/changes.cljs
Normal file
222
frontend/src/app/main/data/workspace/changes.cljs
Normal file
|
@ -0,0 +1,222 @@
|
|||
;; This Source Code Form is subject to the terms of the Mozilla Public
|
||||
;; License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
;; file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
;;
|
||||
;; Copyright (c) UXBOX Labs SL
|
||||
|
||||
(ns app.main.data.workspace.changes
|
||||
(:require
|
||||
[app.common.data :as d]
|
||||
[app.common.pages :as cp]
|
||||
[app.common.pages.spec :as spec]
|
||||
[app.common.spec :as us]
|
||||
[app.main.data.workspace.undo :as dwu]
|
||||
[app.main.worker :as uw]
|
||||
[app.util.logging :as log]
|
||||
[beicon.core :as rx]
|
||||
[cljs.spec.alpha :as s]
|
||||
[clojure.set :as set]
|
||||
[potok.core :as ptk]))
|
||||
|
||||
;; Change this to :info :debug or :trace to debug this module
|
||||
(log/set-level! :warn)
|
||||
|
||||
(s/def ::coll-of-uuid
|
||||
(s/every ::us/uuid))
|
||||
|
||||
(defonce page-change? #{:add-page :mod-page :del-page :mov-page})
|
||||
|
||||
(declare commit-changes)
|
||||
|
||||
(def commit-changes? (ptk/type? ::commit-changes))
|
||||
|
||||
(defn- generate-operations
|
||||
([ma mb] (generate-operations ma mb false))
|
||||
([ma mb undo?]
|
||||
(let [ops (let [ma-keys (set (keys ma))
|
||||
mb-keys (set (keys mb))
|
||||
added (set/difference mb-keys ma-keys)
|
||||
removed (set/difference ma-keys mb-keys)
|
||||
both (set/intersection ma-keys mb-keys)]
|
||||
(d/concat
|
||||
(mapv #(array-map :type :set :attr % :val (get mb %)) added)
|
||||
(mapv #(array-map :type :set :attr % :val nil) removed)
|
||||
(loop [items (seq both)
|
||||
result []]
|
||||
(if items
|
||||
(let [k (first items)
|
||||
vma (get ma k)
|
||||
vmb (get mb k)]
|
||||
(if (= vma vmb)
|
||||
(recur (next items) result)
|
||||
(recur (next items)
|
||||
(conj result {:type :set
|
||||
:attr k
|
||||
:val vmb
|
||||
:ignore-touched undo?}))))
|
||||
result))))]
|
||||
(if undo?
|
||||
(conj ops {:type :set-touched :touched (:touched mb)})
|
||||
ops))))
|
||||
|
||||
(defn update-shapes
|
||||
([ids f] (update-shapes ids f nil))
|
||||
([ids f {:keys [reg-objects?] :or {reg-objects? false}}]
|
||||
(us/assert ::coll-of-uuid ids)
|
||||
(us/assert fn? f)
|
||||
(ptk/reify ::update-shapes
|
||||
ptk/WatchEvent
|
||||
(watch [_ state stream]
|
||||
(let [page-id (:current-page-id state)
|
||||
objects (get-in state [:workspace-data :pages-index page-id :objects])
|
||||
reg-objects {:type :reg-objects :page-id page-id :shapes (vec ids)}]
|
||||
(loop [ids (seq ids)
|
||||
rch []
|
||||
uch []]
|
||||
(if (nil? ids)
|
||||
(rx/of (let [has-rch? (not (empty? rch))
|
||||
has-uch? (not (empty? uch))
|
||||
rch (cond-> rch (and has-rch? reg-objects?) (conj reg-objects))
|
||||
uch (cond-> uch (and has-rch? reg-objects?) (conj reg-objects))]
|
||||
(when (and has-rch? has-uch?)
|
||||
(commit-changes rch uch {:commit-local? true}))))
|
||||
|
||||
(let [id (first ids)
|
||||
obj1 (get objects id)
|
||||
obj2 (f obj1)
|
||||
rch-operations (generate-operations obj1 obj2)
|
||||
uch-operations (generate-operations obj2 obj1 true)
|
||||
rchg {:type :mod-obj
|
||||
:page-id page-id
|
||||
:operations rch-operations
|
||||
:id id}
|
||||
uchg {:type :mod-obj
|
||||
:page-id page-id
|
||||
:operations uch-operations
|
||||
:id id}]
|
||||
(recur (next ids)
|
||||
(if (empty? rch-operations) rch (conj rch rchg))
|
||||
(if (empty? uch-operations) uch (conj uch uchg)))))))))))
|
||||
|
||||
(defn update-shapes-recursive
|
||||
[ids f]
|
||||
(us/assert ::coll-of-uuid ids)
|
||||
(us/assert fn? f)
|
||||
(letfn [(impl-get-children [objects id]
|
||||
(cons id (cp/get-children id objects)))
|
||||
|
||||
(impl-gen-changes [objects page-id ids]
|
||||
(loop [sids (seq ids)
|
||||
cids (seq (impl-get-children objects (first sids)))
|
||||
rchanges []
|
||||
uchanges []]
|
||||
(cond
|
||||
(nil? sids)
|
||||
[rchanges uchanges]
|
||||
|
||||
(nil? cids)
|
||||
(recur (next sids)
|
||||
(seq (impl-get-children objects (first (next sids))))
|
||||
rchanges
|
||||
uchanges)
|
||||
|
||||
:else
|
||||
(let [id (first cids)
|
||||
obj1 (get objects id)
|
||||
obj2 (f obj1)
|
||||
rops (generate-operations obj1 obj2)
|
||||
uops (generate-operations obj2 obj1 true)
|
||||
rchg {:type :mod-obj
|
||||
:page-id page-id
|
||||
:operations rops
|
||||
:id id}
|
||||
uchg {:type :mod-obj
|
||||
:page-id page-id
|
||||
:operations uops
|
||||
:id id}]
|
||||
(recur sids
|
||||
(next cids)
|
||||
(conj rchanges rchg)
|
||||
(conj uchanges uchg))))))]
|
||||
(ptk/reify ::update-shapes-recursive
|
||||
ptk/WatchEvent
|
||||
(watch [_ state stream]
|
||||
(let [page-id (:current-page-id state)
|
||||
objects (get-in state [:workspace-data :pages-index page-id :objects])
|
||||
[rchanges uchanges] (impl-gen-changes objects page-id (seq ids))]
|
||||
(rx/of (commit-changes rchanges uchanges {:commit-local? true})))))))
|
||||
|
||||
(defn update-indices
|
||||
[page-id changes]
|
||||
(ptk/reify ::update-indices
|
||||
ptk/EffectEvent
|
||||
(effect [_ state stream]
|
||||
(uw/ask! {:cmd :update-page-indices
|
||||
:page-id page-id
|
||||
:changes changes}))))
|
||||
|
||||
(defn commit-changes
|
||||
([changes undo-changes]
|
||||
(commit-changes changes undo-changes {}))
|
||||
([changes undo-changes {:keys [save-undo?
|
||||
commit-local?
|
||||
file-id]
|
||||
:or {save-undo? true
|
||||
commit-local? false}
|
||||
:as opts}]
|
||||
(us/assert ::cp/changes changes)
|
||||
(us/assert ::cp/changes undo-changes)
|
||||
(log/debug :msg "commit-changes"
|
||||
:js/changes changes
|
||||
:js/undo-changes undo-changes)
|
||||
|
||||
(let [error (volatile! nil)]
|
||||
(ptk/reify ::commit-changes
|
||||
cljs.core/IDeref
|
||||
(-deref [_] {:file-id file-id :changes changes})
|
||||
|
||||
ptk/UpdateEvent
|
||||
(update [_ state]
|
||||
(let [current-file-id (get state :current-file-id)
|
||||
file-id (or file-id current-file-id)
|
||||
path1 (if (= file-id current-file-id)
|
||||
[:workspace-file :data]
|
||||
[:workspace-libraries file-id :data])
|
||||
path2 (if (= file-id current-file-id)
|
||||
[:workspace-data]
|
||||
[:workspace-libraries file-id :data])]
|
||||
(try
|
||||
(us/assert ::spec/changes changes)
|
||||
(let [state (update-in state path1 cp/process-changes changes false)]
|
||||
(cond-> state
|
||||
commit-local? (update-in path2 cp/process-changes changes false)))
|
||||
(catch :default e
|
||||
(vreset! error e)
|
||||
state))))
|
||||
|
||||
ptk/WatchEvent
|
||||
(watch [_ state stream]
|
||||
(when-not @error
|
||||
(let [;; adds page-id to page changes (that have the `id` field instead)
|
||||
add-page-id
|
||||
(fn [{:keys [id type page] :as change}]
|
||||
(cond-> change
|
||||
(page-change? type)
|
||||
(assoc :page-id (or id (:id page)))))
|
||||
|
||||
changes-by-pages
|
||||
(->> changes
|
||||
(map add-page-id)
|
||||
(remove #(nil? (:page-id %)))
|
||||
(group-by :page-id))
|
||||
|
||||
process-page-changes
|
||||
(fn [[page-id changes]]
|
||||
(update-indices page-id changes))]
|
||||
(rx/concat
|
||||
(rx/from (map process-page-changes changes-by-pages))
|
||||
|
||||
(when (and save-undo? (seq undo-changes))
|
||||
(let [entry {:undo-changes undo-changes
|
||||
:redo-changes changes}]
|
||||
(rx/of (dwu/append-undo entry))))))))))))
|
|
@ -13,6 +13,7 @@
|
|||
[app.main.data.modal :as md]
|
||||
[app.main.data.workspace.common :as dwc]
|
||||
[app.main.data.workspace.texts :as dwt]
|
||||
[app.main.data.workspace.changes :as dch]
|
||||
[app.main.repo :as rp]
|
||||
[app.main.store :as st]
|
||||
[app.main.streams :as ms]
|
||||
|
@ -150,7 +151,7 @@
|
|||
|
||||
(rx/concat
|
||||
(rx/from (map #(dwt/update-text-attrs {:id % :attrs attrs}) text-ids))
|
||||
(rx/of (dwc/update-shapes shape-ids (fn [shape] (d/merge shape attrs)))))))))
|
||||
(rx/of (dch/update-shapes shape-ids (fn [shape] (d/merge shape attrs)))))))))
|
||||
|
||||
(defn change-stroke
|
||||
[ids color]
|
||||
|
@ -176,7 +177,7 @@
|
|||
(contains? color :opacity)
|
||||
(assoc :stroke-opacity (:opacity color)))]
|
||||
|
||||
(rx/of (dwc/update-shapes ids (fn [shape]
|
||||
(rx/of (dch/update-shapes ids (fn [shape]
|
||||
(cond-> (d/merge shape attrs)
|
||||
(= (:stroke-style shape) :none)
|
||||
(assoc :stroke-style :solid
|
||||
|
|
|
@ -10,17 +10,15 @@
|
|||
[app.common.geom.proportions :as gpr]
|
||||
[app.common.geom.shapes :as gsh]
|
||||
[app.common.pages :as cp]
|
||||
[app.common.pages.spec :as spec]
|
||||
[app.common.spec :as us]
|
||||
[app.common.uuid :as uuid]
|
||||
[app.main.worker :as uw]
|
||||
[app.main.data.workspace.changes :as dch]
|
||||
[app.main.data.workspace.undo :as dwu]
|
||||
[app.main.streams :as ms]
|
||||
[app.main.worker :as uw]
|
||||
[app.util.logging :as log]
|
||||
[app.util.timers :as ts]
|
||||
[beicon.core :as rx]
|
||||
[cljs.spec.alpha :as s]
|
||||
[clojure.set :as set]
|
||||
[cuerdas.core :as str]
|
||||
[potok.core :as ptk]))
|
||||
|
||||
;; Change this to :info :debug or :trace to debug this module
|
||||
|
@ -58,121 +56,6 @@
|
|||
(get-in state [:workspace-data :components component-id :objects])))
|
||||
|
||||
|
||||
;; --- Changes Handling
|
||||
|
||||
(defonce page-change? #{:add-page :mod-page :del-page :mov-page})
|
||||
|
||||
(defn commit-changes
|
||||
([changes undo-changes]
|
||||
(commit-changes changes undo-changes {}))
|
||||
([changes undo-changes {:keys [save-undo?
|
||||
commit-local?
|
||||
file-id]
|
||||
:or {save-undo? true
|
||||
commit-local? false}
|
||||
:as opts}]
|
||||
(us/verify ::cp/changes changes)
|
||||
(us/verify ::cp/changes undo-changes)
|
||||
(log/debug :msg "commit-changes"
|
||||
:js/changes changes
|
||||
:js/undo-changes undo-changes)
|
||||
|
||||
(let [error (volatile! nil)]
|
||||
(ptk/reify ::commit-changes
|
||||
cljs.core/IDeref
|
||||
(-deref [_] {:file-id file-id :changes changes})
|
||||
|
||||
ptk/UpdateEvent
|
||||
(update [_ state]
|
||||
(let [current-file-id (get state :current-file-id)
|
||||
file-id (or file-id current-file-id)
|
||||
path1 (if (= file-id current-file-id)
|
||||
[:workspace-file :data]
|
||||
[:workspace-libraries file-id :data])
|
||||
path2 (if (= file-id current-file-id)
|
||||
[:workspace-data]
|
||||
[:workspace-libraries file-id :data])]
|
||||
(try
|
||||
(us/verify ::spec/changes changes)
|
||||
(let [state (update-in state path1 cp/process-changes changes false)]
|
||||
(cond-> state
|
||||
commit-local? (update-in path2 cp/process-changes changes false)))
|
||||
(catch :default e
|
||||
(vreset! error e)
|
||||
state))))
|
||||
|
||||
ptk/WatchEvent
|
||||
(watch [_ state stream]
|
||||
(when-not @error
|
||||
(let [;; adds page-id to page changes (that have the `id` field instead)
|
||||
add-page-id
|
||||
(fn [{:keys [id type page] :as change}]
|
||||
(cond-> change
|
||||
(page-change? type)
|
||||
(assoc :page-id (or id (:id page)))))
|
||||
|
||||
changes-by-pages
|
||||
(->> changes
|
||||
(map add-page-id)
|
||||
(remove #(nil? (:page-id %)))
|
||||
(group-by :page-id))
|
||||
|
||||
process-page-changes
|
||||
(fn [[page-id changes]]
|
||||
(update-indices page-id changes))]
|
||||
(rx/concat
|
||||
(rx/from (map process-page-changes changes-by-pages))
|
||||
|
||||
(when (and save-undo? (seq undo-changes))
|
||||
(let [entry {:undo-changes undo-changes
|
||||
:redo-changes changes}]
|
||||
(rx/of (append-undo entry))))))))))))
|
||||
|
||||
(defn generate-operations
|
||||
([ma mb] (generate-operations ma mb false))
|
||||
([ma mb undo?]
|
||||
(let [ops (let [ma-keys (set (keys ma))
|
||||
mb-keys (set (keys mb))
|
||||
added (set/difference mb-keys ma-keys)
|
||||
removed (set/difference ma-keys mb-keys)
|
||||
both (set/intersection ma-keys mb-keys)]
|
||||
(d/concat
|
||||
(mapv #(array-map :type :set :attr % :val (get mb %)) added)
|
||||
(mapv #(array-map :type :set :attr % :val nil) removed)
|
||||
(loop [items (seq both)
|
||||
result []]
|
||||
(if items
|
||||
(let [k (first items)
|
||||
vma (get ma k)
|
||||
vmb (get mb k)]
|
||||
(if (= vma vmb)
|
||||
(recur (next items) result)
|
||||
(recur (next items)
|
||||
(conj result {:type :set
|
||||
:attr k
|
||||
:val vmb
|
||||
:ignore-touched undo?}))))
|
||||
result))))]
|
||||
(if undo?
|
||||
(conj ops {:type :set-touched :touched (:touched mb)})
|
||||
ops))))
|
||||
|
||||
(defn generate-changes
|
||||
[page-id objects1 objects2]
|
||||
(letfn [(impl-diff [res id]
|
||||
(let [obj1 (get objects1 id)
|
||||
obj2 (get objects2 id)
|
||||
ops (generate-operations (dissoc obj1 :shapes :frame-id)
|
||||
(dissoc obj2 :shapes :frame-id))]
|
||||
(if (empty? ops)
|
||||
res
|
||||
(conj res {:type :mod-obj
|
||||
:page-id page-id
|
||||
:operations ops
|
||||
:id id}))))]
|
||||
(reduce impl-diff [] (set/union (set (keys objects1))
|
||||
(set (keys objects2))))))
|
||||
|
||||
;; --- Selection Index Handling
|
||||
|
||||
(defn initialize-indices
|
||||
|
@ -186,14 +69,7 @@
|
|||
(->> (uw/ask! msg)
|
||||
(rx/map (constantly ::index-initialized)))))))
|
||||
|
||||
(defn update-indices
|
||||
[page-id changes]
|
||||
(ptk/reify ::update-indices
|
||||
ptk/EffectEvent
|
||||
(effect [_ state stream]
|
||||
(uw/ask! {:cmd :update-page-indices
|
||||
:page-id page-id
|
||||
:changes changes}))))
|
||||
|
||||
|
||||
;; --- Common Helpers & Events
|
||||
|
||||
|
@ -253,110 +129,8 @@
|
|||
(update state :workspace-local dissoc :expanded))))
|
||||
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;; Undo / Redo
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
||||
(s/def ::undo-changes ::cp/changes)
|
||||
(s/def ::redo-changes ::cp/changes)
|
||||
(s/def ::undo-entry
|
||||
(s/keys :req-un [::undo-changes ::redo-changes]))
|
||||
|
||||
(def MAX-UNDO-SIZE 50)
|
||||
|
||||
(defn- conj-undo-entry
|
||||
[undo data]
|
||||
(let [undo (conj undo data)
|
||||
cnt (count undo)]
|
||||
(if (> cnt MAX-UNDO-SIZE)
|
||||
(subvec undo (- cnt MAX-UNDO-SIZE))
|
||||
undo)))
|
||||
|
||||
(defn- materialize-undo
|
||||
[changes index]
|
||||
(ptk/reify ::materialize-undo
|
||||
ptk/UpdateEvent
|
||||
(update [_ state]
|
||||
(-> state
|
||||
(update :workspace-data cp/process-changes changes)
|
||||
(assoc-in [:workspace-undo :index] index)))))
|
||||
|
||||
(defn- reset-undo
|
||||
[index]
|
||||
(ptk/reify ::reset-undo
|
||||
ptk/UpdateEvent
|
||||
(update [_ state]
|
||||
(-> state
|
||||
(update :workspace-undo dissoc :undo-index)
|
||||
(update-in [:workspace-undo :items] (fn [queue] (into [] (take (inc index) queue))))))))
|
||||
|
||||
(defn- add-undo-entry
|
||||
[state entry]
|
||||
(if (and entry
|
||||
(not-empty (:undo-changes entry))
|
||||
(not-empty (:redo-changes entry)))
|
||||
(let [index (get-in state [:workspace-undo :index] -1)
|
||||
items (get-in state [:workspace-undo :items] [])
|
||||
items (->> items (take (inc index)) (into []))
|
||||
items (conj-undo-entry items entry)]
|
||||
(-> state
|
||||
(update :workspace-undo assoc :items items
|
||||
:index (min (inc index)
|
||||
(dec MAX-UNDO-SIZE)))))
|
||||
state))
|
||||
|
||||
(defn- accumulate-undo-entry
|
||||
[state {:keys [undo-changes redo-changes]}]
|
||||
(-> state
|
||||
(update-in [:workspace-undo :transaction :undo-changes] #(into undo-changes %))
|
||||
(update-in [:workspace-undo :transaction :redo-changes] #(into % redo-changes))))
|
||||
|
||||
(defn- append-undo
|
||||
[entry]
|
||||
(us/verify ::undo-entry entry)
|
||||
(ptk/reify ::append-undo
|
||||
ptk/UpdateEvent
|
||||
(update [_ state]
|
||||
(if (get-in state [:workspace-undo :transaction])
|
||||
(accumulate-undo-entry state entry)
|
||||
(add-undo-entry state entry)))))
|
||||
|
||||
(defonce empty-tx {:undo-changes [] :redo-changes []})
|
||||
|
||||
(defn start-undo-transaction []
|
||||
(ptk/reify ::start-undo-transaction
|
||||
ptk/UpdateEvent
|
||||
(update [_ state]
|
||||
;; We commit the old transaction before starting the new one
|
||||
(let [current-tx (get-in state [:workspace-undo :transaction])]
|
||||
(cond-> state
|
||||
(nil? current-tx) (assoc-in [:workspace-undo :transaction] empty-tx))))))
|
||||
|
||||
(defn discard-undo-transaction []
|
||||
(ptk/reify ::discard-undo-transaction
|
||||
ptk/UpdateEvent
|
||||
(update [_ state]
|
||||
(update state :workspace-undo dissoc :transaction))))
|
||||
|
||||
(defn commit-undo-transaction []
|
||||
(ptk/reify ::commit-undo-transaction
|
||||
ptk/UpdateEvent
|
||||
(update [_ state]
|
||||
(-> state
|
||||
(add-undo-entry (get-in state [:workspace-undo :transaction]))
|
||||
(update :workspace-undo dissoc :transaction)))))
|
||||
|
||||
(def pop-undo-into-transaction
|
||||
(ptk/reify ::last-undo-into-transaction
|
||||
ptk/UpdateEvent
|
||||
(update [_ state]
|
||||
(let [index (get-in state [:workspace-undo :index] -1)]
|
||||
|
||||
(cond-> state
|
||||
(>= index 0) (accumulate-undo-entry (get-in state [:workspace-undo :items index]))
|
||||
(>= index 0) (update-in [:workspace-undo :index] dec))))))
|
||||
|
||||
;; If these functions change modules review /src/app/main/data/workspace/path/undo.cljs
|
||||
;; These functions should've been in `src/app/main/data/workspace/undo.cljs` but doing that causes
|
||||
;; a circular dependency with `src/app/main/data/workspace/changes.cljs`
|
||||
(def undo
|
||||
(ptk/reify ::undo
|
||||
ptk/WatchEvent
|
||||
|
@ -370,8 +144,8 @@
|
|||
index (or (:index undo) (dec (count items)))]
|
||||
(when-not (or (empty? items) (= index -1))
|
||||
(let [changes (get-in items [index :undo-changes])]
|
||||
(rx/of (materialize-undo changes (dec index))
|
||||
(commit-changes changes [] {:save-undo? false}))))))))))
|
||||
(rx/of (dwu/materialize-undo changes (dec index))
|
||||
(dch/commit-changes changes [] {:save-undo? false}))))))))))
|
||||
|
||||
(def redo
|
||||
(ptk/reify ::redo
|
||||
|
@ -385,16 +159,8 @@
|
|||
index (or (:index undo) (dec (count items)))]
|
||||
(when-not (or (empty? items) (= index (dec (count items))))
|
||||
(let [changes (get-in items [(inc index) :redo-changes])]
|
||||
(rx/of (materialize-undo changes (inc index))
|
||||
(commit-changes changes [] {:save-undo? false}))))))))))
|
||||
|
||||
(def reinitialize-undo
|
||||
(ptk/reify ::reset-undo
|
||||
ptk/UpdateEvent
|
||||
(update [_ state]
|
||||
(assoc state :workspace-undo {}))))
|
||||
|
||||
|
||||
(rx/of (dwu/materialize-undo changes (inc index))
|
||||
(dch/commit-changes changes [] {:save-undo? false}))))))))))
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;; Shapes
|
||||
|
@ -420,93 +186,6 @@
|
|||
;; NOTE: This is a generic implementation for update multiple shapes
|
||||
;; in one single commit/undo entry.
|
||||
|
||||
(s/def ::coll-of-uuid
|
||||
(s/every ::us/uuid))
|
||||
|
||||
(defn update-shapes
|
||||
([ids f] (update-shapes ids f nil))
|
||||
([ids f {:keys [reg-objects?] :or {reg-objects? false}}]
|
||||
(us/assert ::coll-of-uuid ids)
|
||||
(us/assert fn? f)
|
||||
(ptk/reify ::update-shapes
|
||||
ptk/WatchEvent
|
||||
(watch [_ state stream]
|
||||
(let [page-id (:current-page-id state)
|
||||
objects (lookup-page-objects state page-id)]
|
||||
(loop [ids (seq ids)
|
||||
rch []
|
||||
uch []]
|
||||
(if (nil? ids)
|
||||
(rx/of (commit-changes
|
||||
(cond-> rch reg-objects? (conj {:type :reg-objects :page-id page-id :shapes (vec ids)}))
|
||||
(cond-> uch reg-objects? (conj {:type :reg-objects :page-id page-id :shapes (vec ids)}))
|
||||
{:commit-local? true}))
|
||||
|
||||
(let [id (first ids)
|
||||
obj1 (get objects id)
|
||||
obj2 (f obj1)
|
||||
rch-operations (generate-operations obj1 obj2)
|
||||
uch-operations (generate-operations obj2 obj1 true)
|
||||
rchg {:type :mod-obj
|
||||
:page-id page-id
|
||||
:operations rch-operations
|
||||
:id id}
|
||||
uchg {:type :mod-obj
|
||||
:page-id page-id
|
||||
:operations uch-operations
|
||||
:id id}]
|
||||
(recur (next ids)
|
||||
(if (empty? rch-operations) rch (conj rch rchg))
|
||||
(if (empty? uch-operations) uch (conj uch uchg)))))))))))
|
||||
|
||||
(defn update-shapes-recursive
|
||||
[ids f]
|
||||
(us/assert ::coll-of-uuid ids)
|
||||
(us/assert fn? f)
|
||||
(letfn [(impl-get-children [objects id]
|
||||
(cons id (cp/get-children id objects)))
|
||||
|
||||
(impl-gen-changes [objects page-id ids]
|
||||
(loop [sids (seq ids)
|
||||
cids (seq (impl-get-children objects (first sids)))
|
||||
rchanges []
|
||||
uchanges []]
|
||||
(cond
|
||||
(nil? sids)
|
||||
[rchanges uchanges]
|
||||
|
||||
(nil? cids)
|
||||
(recur (next sids)
|
||||
(seq (impl-get-children objects (first (next sids))))
|
||||
rchanges
|
||||
uchanges)
|
||||
|
||||
:else
|
||||
(let [id (first cids)
|
||||
obj1 (get objects id)
|
||||
obj2 (f obj1)
|
||||
rops (generate-operations obj1 obj2)
|
||||
uops (generate-operations obj2 obj1 true)
|
||||
rchg {:type :mod-obj
|
||||
:page-id page-id
|
||||
:operations rops
|
||||
:id id}
|
||||
uchg {:type :mod-obj
|
||||
:page-id page-id
|
||||
:operations uops
|
||||
:id id}]
|
||||
(recur sids
|
||||
(next cids)
|
||||
(conj rchanges rchg)
|
||||
(conj uchanges uchg))))))]
|
||||
(ptk/reify ::update-shapes-recursive
|
||||
ptk/WatchEvent
|
||||
(watch [_ state stream]
|
||||
(let [page-id (:current-page-id state)
|
||||
objects (lookup-page-objects state page-id)
|
||||
[rchanges uchanges] (impl-gen-changes objects page-id (seq ids))]
|
||||
(rx/of (commit-changes rchanges uchanges {:commit-local? true})))))))
|
||||
|
||||
|
||||
(defn select-shapes
|
||||
[ids]
|
||||
|
@ -639,7 +318,7 @@
|
|||
(assoc :name name)))]
|
||||
|
||||
(rx/concat
|
||||
(rx/of (commit-changes rchanges uchanges {:commit-local? true})
|
||||
(rx/of (dch/commit-changes rchanges uchanges {:commit-local? true})
|
||||
(select-shapes (d/ordered-set id)))
|
||||
(when (= :text (:type attrs))
|
||||
(->> (rx/of (start-edition-mode id))
|
||||
|
@ -672,7 +351,7 @@
|
|||
:page-id page-id
|
||||
:index index
|
||||
:shapes [shape-id]})))]
|
||||
(rx/of (commit-changes rchanges uchanges {:commit-local? true}))))))
|
||||
(rx/of (dch/commit-changes rchanges uchanges {:commit-local? true}))))))
|
||||
|
||||
|
||||
(defn delete-shapes
|
||||
|
@ -779,7 +458,7 @@
|
|||
;; (cljs.pprint/pprint rchanges)
|
||||
;; (println "================ uchanges")
|
||||
;; (cljs.pprint/pprint uchanges)
|
||||
(rx/of (commit-changes rchanges uchanges {:commit-local? true}))))))
|
||||
(rx/of (dch/commit-changes rchanges uchanges {:commit-local? true}))))))
|
||||
|
||||
|
||||
;; --- Add shape to Workspace
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
[app.common.geom.shapes :as gsh]
|
||||
[app.main.data.workspace.common :as dwc]
|
||||
[app.main.data.workspace.selection :as dws]
|
||||
[app.main.data.workspace.undo :as dwu]
|
||||
[app.main.streams :as ms]
|
||||
[app.main.worker :as uw]))
|
||||
|
||||
|
@ -54,7 +55,7 @@
|
|||
;; Add & select the created shape to the workspace
|
||||
(rx/concat
|
||||
(if (= :text (:type shape))
|
||||
(rx/of (dwc/start-undo-transaction))
|
||||
(rx/of (dwu/start-undo-transaction))
|
||||
(rx/empty))
|
||||
|
||||
(rx/of (dwc/add-shape shape))
|
||||
|
|
|
@ -10,7 +10,7 @@
|
|||
[potok.core :as ptk]
|
||||
[app.common.data :as d]
|
||||
[app.common.spec :as us]
|
||||
[app.main.data.workspace.common :as dwc]))
|
||||
[app.main.data.workspace.changes :as dch]))
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;; Grid
|
||||
|
@ -48,7 +48,7 @@
|
|||
grid {:type :square
|
||||
:params params
|
||||
:display true}]
|
||||
(rx/of (dwc/update-shapes [frame-id]
|
||||
(rx/of (dch/update-shapes [frame-id]
|
||||
(fn [obj] (update obj :grids (fnil #(conj % grid) [])))))))))
|
||||
|
||||
|
||||
|
@ -57,14 +57,14 @@
|
|||
(ptk/reify ::set-frame-grid
|
||||
ptk/WatchEvent
|
||||
(watch [_ state stream]
|
||||
(rx/of (dwc/update-shapes [frame-id] (fn [o] (update o :grids (fnil #(d/remove-at-index % index) []))))))))
|
||||
(rx/of (dch/update-shapes [frame-id] (fn [o] (update o :grids (fnil #(d/remove-at-index % index) []))))))))
|
||||
|
||||
(defn set-frame-grid
|
||||
[frame-id index data]
|
||||
(ptk/reify ::set-frame-grid
|
||||
ptk/WatchEvent
|
||||
(watch [_ state stream]
|
||||
(rx/of (dwc/update-shapes [frame-id] #(assoc-in % [:grids index] data))))))
|
||||
(rx/of (dch/update-shapes [frame-id] #(assoc-in % [:grids index] data))))))
|
||||
|
||||
(defn set-default-grid
|
||||
[type params]
|
||||
|
@ -73,7 +73,7 @@
|
|||
(watch [_ state stream]
|
||||
(let [pid (:current-page-id state)
|
||||
prev-value (get-in state [:workspace-data :pages-index pid :options :saved-grids type])]
|
||||
(rx/of (dwc/commit-changes [{:type :set-option
|
||||
(rx/of (dch/commit-changes [{:type :set-option
|
||||
:page-id pid
|
||||
:option [:saved-grids type]
|
||||
:value params}]
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
[app.common.geom.shapes :as gsh]
|
||||
[app.common.pages :as cp]
|
||||
[app.main.data.workspace.common :as dwc]
|
||||
[app.main.data.workspace.changes :as dch]
|
||||
[app.main.data.workspace.selection :as dws]
|
||||
[beicon.core :as rx]
|
||||
[potok.core :as ptk]))
|
||||
|
@ -106,7 +107,7 @@
|
|||
shapes (shapes-for-grouping objects selected)]
|
||||
(when-not (empty? shapes)
|
||||
(let [[group rchanges uchanges] (prepare-create-group page-id shapes "Group-" false)]
|
||||
(rx/of (dwc/commit-changes rchanges uchanges {:commit-local? true})
|
||||
(rx/of (dch/commit-changes rchanges uchanges {:commit-local? true})
|
||||
(dwc/select-shapes (d/ordered-set (:id group))))))))))
|
||||
|
||||
(def ungroup-selected
|
||||
|
@ -122,7 +123,7 @@
|
|||
(= (:type group) :group))
|
||||
(let [[rchanges uchanges]
|
||||
(prepare-remove-group page-id group objects)]
|
||||
(rx/of (dwc/commit-changes rchanges uchanges {:commit-local? true}))))))))
|
||||
(rx/of (dch/commit-changes rchanges uchanges {:commit-local? true}))))))))
|
||||
|
||||
(def mask-group
|
||||
(ptk/reify ::mask-group
|
||||
|
@ -176,7 +177,7 @@
|
|||
:page-id page-id
|
||||
:shapes [(:id group)]})]
|
||||
|
||||
(rx/of (dwc/commit-changes rchanges uchanges {:commit-local? true})
|
||||
(rx/of (dch/commit-changes rchanges uchanges {:commit-local? true})
|
||||
(dwc/select-shapes (d/ordered-set (:id group))))))))))
|
||||
|
||||
(def unmask-group
|
||||
|
@ -209,7 +210,7 @@
|
|||
:page-id page-id
|
||||
:shapes [(:id group)]}]]
|
||||
|
||||
(rx/of (dwc/commit-changes rchanges uchanges {:commit-local? true})
|
||||
(rx/of (dch/commit-changes rchanges uchanges {:commit-local? true})
|
||||
(dwc/select-shapes (d/ordered-set (:id group))))))))))
|
||||
|
||||
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
[app.common.geom.shapes :as geom]
|
||||
[app.main.data.messages :as dm]
|
||||
[app.main.data.workspace.common :as dwc]
|
||||
[app.main.data.workspace.changes :as dch]
|
||||
[app.main.data.workspace.libraries-helpers :as dwlh]
|
||||
[app.common.pages :as cp]
|
||||
[app.main.repo :as rp]
|
||||
|
@ -90,7 +91,7 @@
|
|||
uchg {:type :del-color
|
||||
:id id}]
|
||||
(rx/of #(assoc-in % [:workspace-local :color-for-rename] id)
|
||||
(dwc/commit-changes [rchg] [uchg] {:commit-local? true})))))))
|
||||
(dch/commit-changes [rchg] [uchg] {:commit-local? true})))))))
|
||||
|
||||
(defn add-recent-color
|
||||
[color]
|
||||
|
@ -100,7 +101,7 @@
|
|||
(watch [_ state s]
|
||||
(let [rchg {:type :add-recent-color
|
||||
:color color}]
|
||||
(rx/of (dwc/commit-changes [rchg] [] {:commit-local? true}))))))
|
||||
(rx/of (dch/commit-changes [rchg] [] {:commit-local? true}))))))
|
||||
|
||||
(def clear-color-for-rename
|
||||
(ptk/reify ::clear-color-for-rename
|
||||
|
@ -120,7 +121,7 @@
|
|||
:color color}
|
||||
uchg {:type :mod-color
|
||||
:color prev}]
|
||||
(rx/of (dwc/commit-changes [rchg] [uchg] {:commit-local? true})
|
||||
(rx/of (dch/commit-changes [rchg] [uchg] {:commit-local? true})
|
||||
(sync-file (:current-file-id state) file-id))))))
|
||||
|
||||
(defn delete-color
|
||||
|
@ -134,7 +135,7 @@
|
|||
:id id}
|
||||
uchg {:type :add-color
|
||||
:color prev}]
|
||||
(rx/of (dwc/commit-changes [rchg] [uchg] {:commit-local? true}))))))
|
||||
(rx/of (dch/commit-changes [rchg] [uchg] {:commit-local? true}))))))
|
||||
|
||||
(defn add-media
|
||||
[{:keys [id] :as media}]
|
||||
|
@ -147,7 +148,7 @@
|
|||
:object obj}
|
||||
uchg {:type :del-media
|
||||
:id id}]
|
||||
(rx/of (dwc/commit-changes [rchg] [uchg] {:commit-local? true}))))))
|
||||
(rx/of (dch/commit-changes [rchg] [uchg] {:commit-local? true}))))))
|
||||
|
||||
(defn rename-media
|
||||
[id new-name]
|
||||
|
@ -169,7 +170,7 @@
|
|||
:name (:name object)
|
||||
:path (:path object)}}]]
|
||||
|
||||
(rx/of (dwc/commit-changes rchanges uchanges {:commit-local? true}))))))
|
||||
(rx/of (dch/commit-changes rchanges uchanges {:commit-local? true}))))))
|
||||
|
||||
(defn delete-media
|
||||
[{:keys [id] :as params}]
|
||||
|
@ -182,7 +183,7 @@
|
|||
:id id}
|
||||
uchg {:type :add-media
|
||||
:object prev}]
|
||||
(rx/of (dwc/commit-changes [rchg] [uchg] {:commit-local? true}))))))
|
||||
(rx/of (dch/commit-changes [rchg] [uchg] {:commit-local? true}))))))
|
||||
|
||||
(defn add-typography
|
||||
([typography] (add-typography typography true))
|
||||
|
@ -196,7 +197,7 @@
|
|||
:typography typography}
|
||||
uchg {:type :del-typography
|
||||
:id (:id typography)}]
|
||||
(rx/of (dwc/commit-changes [rchg] [uchg] {:commit-local? true})
|
||||
(rx/of (dch/commit-changes [rchg] [uchg] {:commit-local? true})
|
||||
#(cond-> %
|
||||
edit?
|
||||
(assoc-in [:workspace-local :rename-typography] (:id typography))))))))))
|
||||
|
@ -213,7 +214,7 @@
|
|||
:typography typography}
|
||||
uchg {:type :mod-typography
|
||||
:typography prev}]
|
||||
(rx/of (dwc/commit-changes [rchg] [uchg] {:commit-local? true})
|
||||
(rx/of (dch/commit-changes [rchg] [uchg] {:commit-local? true})
|
||||
(sync-file (:current-file-id state) file-id))))))
|
||||
|
||||
(defn delete-typography
|
||||
|
@ -227,7 +228,7 @@
|
|||
:id id}
|
||||
uchg {:type :add-typography
|
||||
:typography prev}]
|
||||
(rx/of (dwc/commit-changes [rchg] [uchg] {:commit-local? true}))))))
|
||||
(rx/of (dch/commit-changes [rchg] [uchg] {:commit-local? true}))))))
|
||||
|
||||
(def add-component
|
||||
"Add a new component to current file library, from the currently selected shapes."
|
||||
|
@ -242,7 +243,7 @@
|
|||
(let [[group rchanges uchanges]
|
||||
(dwlh/generate-add-component selected objects page-id file-id)]
|
||||
(when-not (empty? rchanges)
|
||||
(rx/of (dwc/commit-changes rchanges uchanges {:commit-local? true})
|
||||
(rx/of (dch/commit-changes rchanges uchanges {:commit-local? true})
|
||||
(dwc/select-shapes (d/ordered-set (:id group))))))))))
|
||||
|
||||
(defn rename-component
|
||||
|
@ -273,7 +274,7 @@
|
|||
:path (:path component)
|
||||
:objects objects}]]
|
||||
|
||||
(rx/of (dwc/commit-changes rchanges uchanges {:commit-local? true}))))))
|
||||
(rx/of (dch/commit-changes rchanges uchanges {:commit-local? true}))))))
|
||||
|
||||
(defn duplicate-component
|
||||
"Create a new component copied from the one with the given id."
|
||||
|
@ -301,7 +302,7 @@
|
|||
uchanges [{:type :del-component
|
||||
:id (:id new-shape)}]]
|
||||
|
||||
(rx/of (dwc/commit-changes rchanges uchanges {:commit-local? true}))))))
|
||||
(rx/of (dch/commit-changes rchanges uchanges {:commit-local? true}))))))
|
||||
|
||||
(defn delete-component
|
||||
"Delete the component with the given id, from the current file library."
|
||||
|
@ -321,7 +322,7 @@
|
|||
:path (:path component)
|
||||
:shapes (vals (:objects component))}]]
|
||||
|
||||
(rx/of (dwc/commit-changes rchanges uchanges {:commit-local? true}))))))
|
||||
(rx/of (dch/commit-changes rchanges uchanges {:commit-local? true}))))))
|
||||
|
||||
(defn instantiate-component
|
||||
"Create a new shape in the current page, from the component with the given id
|
||||
|
@ -398,7 +399,7 @@
|
|||
:ignore-touched true})
|
||||
new-shapes)]
|
||||
|
||||
(rx/of (dwc/commit-changes rchanges uchanges {:commit-local? true})
|
||||
(rx/of (dch/commit-changes rchanges uchanges {:commit-local? true})
|
||||
(dwc/select-shapes (d/ordered-set (:id new-shape))))))))
|
||||
|
||||
(defn detach-component
|
||||
|
@ -461,7 +462,7 @@
|
|||
:val (:touched obj)}]})
|
||||
shapes)]
|
||||
|
||||
(rx/of (dwc/commit-changes rchanges uchanges {:commit-local? true}))))))
|
||||
(rx/of (dch/commit-changes rchanges uchanges {:commit-local? true}))))))
|
||||
|
||||
(defn nav-to-component-file
|
||||
[file-id]
|
||||
|
@ -514,7 +515,7 @@
|
|||
rchanges
|
||||
local-library))
|
||||
|
||||
(rx/of (dwc/commit-changes rchanges uchanges {:commit-local? true}))))))
|
||||
(rx/of (dch/commit-changes rchanges uchanges {:commit-local? true}))))))
|
||||
|
||||
(defn update-component
|
||||
"Modify the component linked to the shape with the given id, in the
|
||||
|
@ -569,11 +570,11 @@
|
|||
file))
|
||||
|
||||
(rx/of (when (seq local-rchanges)
|
||||
(dwc/commit-changes local-rchanges local-uchanges
|
||||
(dch/commit-changes local-rchanges local-uchanges
|
||||
{:commit-local? true
|
||||
:file-id (:id local-library)}))
|
||||
(when (seq rchanges)
|
||||
(dwc/commit-changes rchanges uchanges
|
||||
(dch/commit-changes rchanges uchanges
|
||||
{:commit-local? true
|
||||
:file-id file-id})))))))
|
||||
|
||||
|
@ -623,7 +624,7 @@
|
|||
(rx/concat
|
||||
(rx/of (dm/hide-tag :sync-dialog))
|
||||
(when rchanges
|
||||
(rx/of (dwc/commit-changes rchanges uchanges {:commit-local? true
|
||||
(rx/of (dch/commit-changes rchanges uchanges {:commit-local? true
|
||||
:file-id file-id})))
|
||||
(when (not= file-id library-id)
|
||||
;; When we have just updated the library file, give some time for the
|
||||
|
@ -666,7 +667,7 @@
|
|||
(log/debug :msg "SYNC-FILE (2nd stage) finished" :js/rchanges (log-changes
|
||||
rchanges
|
||||
file))
|
||||
(rx/of (dwc/commit-changes rchanges uchanges {:commit-local? true
|
||||
(rx/of (dch/commit-changes rchanges uchanges {:commit-local? true
|
||||
:file-id file-id})))))))
|
||||
|
||||
(def ignore-sync
|
||||
|
|
|
@ -24,6 +24,7 @@
|
|||
(d/export edition/start-move-path-point)
|
||||
(d/export edition/start-path-edit)
|
||||
(d/export edition/create-node-at-position)
|
||||
(d/export edition/move-selected)
|
||||
|
||||
;; Selection
|
||||
(d/export selection/handle-selection)
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
(ns app.main.data.workspace.path.changes
|
||||
(:require
|
||||
[app.common.spec :as us]
|
||||
[app.main.data.workspace.common :as dwc]
|
||||
[app.main.data.workspace.changes :as dch]
|
||||
[app.main.data.workspace.path.helpers :as helpers]
|
||||
[app.main.data.workspace.path.spec :as spec]
|
||||
[app.main.data.workspace.path.state :as st]
|
||||
|
@ -67,7 +67,7 @@
|
|||
(let [shape (get-in state (st/get-path state))
|
||||
page-id (:current-page-id state)
|
||||
[rch uch] (generate-path-changes page-id shape old-content (:content shape))]
|
||||
(rx/of (dwc/commit-changes rch uch {:commit-local? true})))
|
||||
(rx/of (dch/commit-changes rch uch {:commit-local? true})))
|
||||
(rx/empty)))))))
|
||||
|
||||
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
[app.common.data :as d]
|
||||
[app.common.geom.point :as gpt]
|
||||
[app.common.math :as mth]
|
||||
[app.main.data.workspace.common :as dwc]
|
||||
[app.main.data.workspace.changes :as dch]
|
||||
[app.main.data.workspace.path.changes :as changes]
|
||||
[app.main.data.workspace.path.common :as common]
|
||||
[app.main.data.workspace.path.helpers :as helpers]
|
||||
|
@ -60,49 +60,65 @@
|
|||
|
||||
[rch uch] (changes/generate-path-changes page-id shape (:content shape) new-content)]
|
||||
|
||||
(rx/of (dwc/commit-changes rch uch {:commit-local? true})
|
||||
(rx/of (dch/commit-changes rch uch {:commit-local? true})
|
||||
(selection/update-selection point-change)
|
||||
(fn [state] (update-in state [:workspace-local :edit-path id] dissoc :content-modifiers :moving-nodes :moving-handler)))))))
|
||||
|
||||
(defn modify-content-point
|
||||
[content {dx :x dy :y} modifiers point]
|
||||
(let [point-indices (upc/point-indices content point) ;; [indices]
|
||||
handler-indices (upc/handler-indices content point) ;; [[index prefix]]
|
||||
|
||||
modify-point
|
||||
(fn [modifiers index]
|
||||
(-> modifiers
|
||||
(update index assoc :x dx :y dy)))
|
||||
|
||||
modify-handler
|
||||
(fn [modifiers [index prefix]]
|
||||
(let [cx (d/prefix-keyword prefix :x)
|
||||
cy (d/prefix-keyword prefix :y)]
|
||||
(-> modifiers
|
||||
(update index assoc cx dx cy dy))))]
|
||||
|
||||
(as-> modifiers $
|
||||
(reduce modify-point $ point-indices)
|
||||
(reduce modify-handler $ handler-indices))))
|
||||
|
||||
(defn set-move-modifier
|
||||
[points move-modifier]
|
||||
(ptk/reify ::set-modifiers
|
||||
ptk/UpdateEvent
|
||||
(update [_ state]
|
||||
(let [id (st/get-path-id state)
|
||||
content (get-in state (st/get-path state :content))
|
||||
modifiers-reducer (partial modify-content-point content move-modifier)
|
||||
content-modifiers (get-in state [:workspace-local :edit-path id :content-modifiers] {})
|
||||
content-modifiers (->> points
|
||||
(reduce modifiers-reducer content-modifiers))]
|
||||
|
||||
(-> state
|
||||
(assoc-in [:workspace-local :edit-path id :content-modifiers] content-modifiers))))))
|
||||
|
||||
(defn move-selected-path-point [from-point to-point]
|
||||
(letfn [(modify-content-point [content {dx :x dy :y} modifiers point]
|
||||
(let [point-indices (upc/point-indices content point) ;; [indices]
|
||||
handler-indices (upc/handler-indices content point) ;; [[index prefix]]
|
||||
(ptk/reify ::move-point
|
||||
ptk/UpdateEvent
|
||||
(update [_ state]
|
||||
(let [id (st/get-path-id state)
|
||||
content (get-in state (st/get-path state :content))
|
||||
delta (gpt/subtract to-point from-point)
|
||||
|
||||
modify-point
|
||||
(fn [modifiers index]
|
||||
(-> modifiers
|
||||
(update index assoc :x dx :y dy)))
|
||||
modifiers-reducer (partial modify-content-point content delta)
|
||||
|
||||
modify-handler
|
||||
(fn [modifiers [index prefix]]
|
||||
(let [cx (d/prefix-keyword prefix :x)
|
||||
cy (d/prefix-keyword prefix :y)]
|
||||
(-> modifiers
|
||||
(update index assoc cx dx cy dy))))]
|
||||
points (get-in state [:workspace-local :edit-path id :selected-points] #{})
|
||||
|
||||
(as-> modifiers $
|
||||
(reduce modify-point $ point-indices)
|
||||
(reduce modify-handler $ handler-indices))))]
|
||||
modifiers (get-in state [:workspace-local :edit-path id :content-modifiers] {})
|
||||
modifiers (->> points
|
||||
(reduce modifiers-reducer modifiers))]
|
||||
|
||||
(ptk/reify ::move-point
|
||||
ptk/UpdateEvent
|
||||
(update [_ state]
|
||||
(let [id (st/get-path-id state)
|
||||
content (get-in state (st/get-path state :content))
|
||||
delta (gpt/subtract to-point from-point)
|
||||
|
||||
modifiers-reducer (partial modify-content-point content delta)
|
||||
|
||||
points (get-in state [:workspace-local :edit-path id :selected-points] #{})
|
||||
|
||||
modifiers (get-in state [:workspace-local :edit-path id :content-modifiers] {})
|
||||
modifiers (->> points
|
||||
(reduce modifiers-reducer {}))]
|
||||
|
||||
(-> state
|
||||
(assoc-in [:workspace-local :edit-path id :moving-nodes] true)
|
||||
(assoc-in [:workspace-local :edit-path id :content-modifiers] modifiers)))))))
|
||||
(-> state
|
||||
(assoc-in [:workspace-local :edit-path id :moving-nodes] true)
|
||||
(assoc-in [:workspace-local :edit-path id :content-modifiers] modifiers))))))
|
||||
|
||||
(declare drag-selected-points)
|
||||
|
||||
|
@ -126,7 +142,6 @@
|
|||
ptk/WatchEvent
|
||||
(watch [_ state stream]
|
||||
(let [stopper (->> stream (rx/filter ms/mouse-up?))
|
||||
zoom (get-in state [:workspace-local :zoom])
|
||||
id (get-in state [:workspace-local :edition])
|
||||
snap-toggled (get-in state [:workspace-local :edit-path id :snap-toggled])
|
||||
|
||||
|
@ -143,6 +158,73 @@
|
|||
(rx/map #(move-selected-path-point start-position %)))
|
||||
(rx/of (apply-content-modifiers)))))))
|
||||
|
||||
(defn- get-displacement
|
||||
"Retrieve the correct displacement delta point for the
|
||||
provided direction speed and distances thresholds."
|
||||
[direction]
|
||||
(case direction
|
||||
:up (gpt/point 0 (- 1))
|
||||
:down (gpt/point 0 1)
|
||||
:left (gpt/point (- 1) 0)
|
||||
:right (gpt/point 1 0)))
|
||||
|
||||
(defn finish-move-selected []
|
||||
(ptk/reify ::move-selected
|
||||
ptk/UpdateEvent
|
||||
(update [_ state]
|
||||
(let [id (get-in state [:workspace-local :edition])]
|
||||
(-> state
|
||||
(update-in [:workspace-local :edit-path id] dissoc :current-move))))))
|
||||
|
||||
(defn move-selected
|
||||
[direction shift?]
|
||||
|
||||
(let [same-event (js/Symbol "same-event")]
|
||||
(ptk/reify ::move-selected
|
||||
IDeref
|
||||
(-deref [_] direction)
|
||||
|
||||
ptk/UpdateEvent
|
||||
(update [_ state]
|
||||
(let [id (get-in state [:workspace-local :edition])
|
||||
current-move (get-in state [:workspace-local :edit-path id :current-move])]
|
||||
(if (nil? current-move)
|
||||
(-> state
|
||||
(assoc-in [:workspace-local :edit-path id :moving-nodes] true)
|
||||
(assoc-in [:workspace-local :edit-path id :current-move] same-event))
|
||||
state)))
|
||||
|
||||
ptk/WatchEvent
|
||||
(watch [_ state stream]
|
||||
(let [id (get-in state [:workspace-local :edition])
|
||||
current-move (get-in state [:workspace-local :edit-path id :current-move])]
|
||||
(if (= same-event current-move)
|
||||
(let [points (get-in state [:workspace-local :edit-path id :selected-points] #{})
|
||||
|
||||
move-events (->> stream
|
||||
(rx/filter (ptk/type? ::move-selected))
|
||||
(rx/filter #(= direction (deref %))))
|
||||
|
||||
stopper (->> move-events (rx/debounce 100) (rx/take 1))
|
||||
|
||||
scale (if shift? (gpt/point 10) (gpt/point 1))
|
||||
|
||||
mov-vec (gpt/multiply (get-displacement direction) scale)]
|
||||
|
||||
(rx/concat
|
||||
(rx/merge
|
||||
(->> move-events
|
||||
(rx/take-until stopper)
|
||||
(rx/scan #(gpt/add %1 mov-vec) (gpt/point 0 0))
|
||||
(rx/map #(set-move-modifier points %)))
|
||||
|
||||
;; First event is not read by the stream so we need to send it again
|
||||
(rx/of (move-selected direction shift?)))
|
||||
|
||||
(rx/of (apply-content-modifiers)
|
||||
(finish-move-selected))))
|
||||
(rx/empty)))))))
|
||||
|
||||
(defn start-move-handler
|
||||
[index prefix]
|
||||
(ptk/reify ::start-move-handler
|
||||
|
|
|
@ -18,9 +18,6 @@
|
|||
[app.util.path.subpaths :as ups]
|
||||
[potok.core :as ptk]))
|
||||
|
||||
;; CONSTANTS
|
||||
(defonce enter-keycode 13)
|
||||
|
||||
(defn end-path-event? [{:keys [type shift] :as event}]
|
||||
(or (= (ptk/type event) ::common/finish-path)
|
||||
(= (ptk/type event) :esc-pressed)
|
||||
|
|
|
@ -32,61 +32,95 @@
|
|||
(rx/empty))))))
|
||||
|
||||
(def shortcuts
|
||||
{:move-nodes {:tooltip "V"
|
||||
:command "v"
|
||||
:fn #(st/emit! (drp/change-edit-mode :move))}
|
||||
{:move-nodes {:tooltip "V"
|
||||
:command "v"
|
||||
:fn #(st/emit! (drp/change-edit-mode :move))}
|
||||
|
||||
:draw-nodes {:tooltip "P"
|
||||
:command "p"
|
||||
:fn #(st/emit! (drp/change-edit-mode :draw))}
|
||||
:draw-nodes {:tooltip "P"
|
||||
:command "p"
|
||||
:fn #(st/emit! (drp/change-edit-mode :draw))}
|
||||
|
||||
:add-node {:tooltip "+"
|
||||
:command "+"
|
||||
:fn #(st/emit! (drp/add-node))}
|
||||
:add-node {:tooltip "+"
|
||||
:command "+"
|
||||
:fn #(st/emit! (drp/add-node))}
|
||||
|
||||
:delete-node {:tooltip (ds/supr)
|
||||
:command ["del" "backspace"]
|
||||
:fn #(st/emit! (drp/remove-node))}
|
||||
:delete-node {:tooltip (ds/supr)
|
||||
:command ["del" "backspace"]
|
||||
:fn #(st/emit! (drp/remove-node))}
|
||||
|
||||
:merge-nodes {:tooltip (ds/meta "J")
|
||||
:command (ds/c-mod "j")
|
||||
:fn #(st/emit! (drp/merge-nodes))}
|
||||
:merge-nodes {:tooltip (ds/meta "J")
|
||||
:command (ds/c-mod "j")
|
||||
:fn #(st/emit! (drp/merge-nodes))}
|
||||
|
||||
:join-nodes {:tooltip "J"
|
||||
:command "j"
|
||||
:fn #(st/emit! (drp/join-nodes))}
|
||||
:join-nodes {:tooltip "J"
|
||||
:command "j"
|
||||
:fn #(st/emit! (drp/join-nodes))}
|
||||
|
||||
:separate-nodes {:tooltip "K"
|
||||
:command "k"
|
||||
:fn #(st/emit! (drp/separate-nodes))}
|
||||
:separate-nodes {:tooltip "K"
|
||||
:command "k"
|
||||
:fn #(st/emit! (drp/separate-nodes))}
|
||||
|
||||
:make-corner {:tooltip "B"
|
||||
:command "b"
|
||||
:fn #(st/emit! (drp/make-corner))}
|
||||
:make-corner {:tooltip "B"
|
||||
:command "b"
|
||||
:fn #(st/emit! (drp/make-corner))}
|
||||
|
||||
:make-curve {:tooltip (ds/meta "B")
|
||||
:command (ds/c-mod "b")
|
||||
:fn #(st/emit! (drp/make-curve))}
|
||||
:make-curve {:tooltip (ds/meta "B")
|
||||
:command (ds/c-mod "b")
|
||||
:fn #(st/emit! (drp/make-curve))}
|
||||
|
||||
:snap-nodes {:tooltip (ds/meta "'")
|
||||
:command (ds/c-mod "'")
|
||||
:fn #(st/emit! (drp/toggle-snap))}
|
||||
|
||||
:escape {:tooltip (ds/esc)
|
||||
:command "escape"
|
||||
:fn #(st/emit! (esc-pressed))}
|
||||
:snap-nodes {:tooltip (ds/meta "'")
|
||||
:command (ds/c-mod "'")
|
||||
:fn #(st/emit! (drp/toggle-snap))}
|
||||
|
||||
:start-editing {:tooltip (ds/enter)
|
||||
:command "enter"
|
||||
:fn #(st/emit! (dw/start-editing-selected))}
|
||||
:escape {:tooltip (ds/esc)
|
||||
:command "escape"
|
||||
:fn #(st/emit! (esc-pressed))}
|
||||
|
||||
:undo {:tooltip (ds/meta "Z")
|
||||
:command (ds/c-mod "z")
|
||||
:fn #(st/emit! (drp/undo-path))}
|
||||
:start-editing {:tooltip (ds/enter)
|
||||
:command "enter"
|
||||
:fn #(st/emit! (dw/start-editing-selected))}
|
||||
|
||||
:undo {:tooltip (ds/meta "Z")
|
||||
:command (ds/c-mod "z")
|
||||
:fn #(st/emit! (drp/undo-path))}
|
||||
|
||||
:redo {:tooltip (ds/meta "Y")
|
||||
:command [(ds/c-mod "shift+z") (ds/c-mod "y")]
|
||||
:fn #(st/emit! (drp/redo-path))}
|
||||
|
||||
;; Arrow movement
|
||||
:move-fast-up {:tooltip (ds/shift ds/up-arrow)
|
||||
:command "shift+up"
|
||||
:fn #(st/emit! (drp/move-selected :up true))}
|
||||
|
||||
:move-fast-down {:tooltip (ds/shift ds/down-arrow)
|
||||
:command "shift+down"
|
||||
:fn #(st/emit! (drp/move-selected :down true))}
|
||||
|
||||
:move-fast-right {:tooltip (ds/shift ds/right-arrow)
|
||||
:command "shift+right"
|
||||
:fn #(st/emit! (drp/move-selected :right true))}
|
||||
|
||||
:move-fast-left {:tooltip (ds/shift ds/left-arrow)
|
||||
:command "shift+left"
|
||||
:fn #(st/emit! (drp/move-selected :left true))}
|
||||
|
||||
:move-unit-up {:tooltip ds/up-arrow
|
||||
:command "up"
|
||||
:fn #(st/emit! (drp/move-selected :up false))}
|
||||
|
||||
:move-unit-down {:tooltip ds/down-arrow
|
||||
:command "down"
|
||||
:fn #(st/emit! (drp/move-selected :down false))}
|
||||
|
||||
:move-unit-left {:tooltip ds/right-arrow
|
||||
:command "right"
|
||||
:fn #(st/emit! (drp/move-selected :right false))}
|
||||
|
||||
:move-unit-right {:tooltip ds/left-arrow
|
||||
:command "left"
|
||||
:fn #(st/emit! (drp/move-selected :left false))}
|
||||
|
||||
:redo {:tooltip (ds/meta "Y")
|
||||
:command [(ds/c-mod "shift+z") (ds/c-mod "y")]
|
||||
:fn #(st/emit! (drp/redo-path))}
|
||||
})
|
||||
|
||||
(defn get-tooltip [shortcut]
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
|
||||
(ns app.main.data.workspace.path.tools
|
||||
(:require
|
||||
[app.main.data.workspace.common :as dwc]
|
||||
[app.main.data.workspace.changes :as dch]
|
||||
[app.main.data.workspace.path.changes :as changes]
|
||||
[app.main.data.workspace.path.common :as common]
|
||||
[app.main.data.workspace.path.state :as st]
|
||||
|
@ -32,7 +32,7 @@
|
|||
new-content (-> (tool-fn (:content shape) points)
|
||||
(ups/close-subpaths))
|
||||
[rch uch] (changes/generate-path-changes page-id shape (:content shape) new-content)]
|
||||
(rx/of (dwc/commit-changes rch uch {:commit-local? true})))))))
|
||||
(rx/of (dch/commit-changes rch uch {:commit-local? true})))))))
|
||||
|
||||
(defn make-corner
|
||||
([]
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
[app.main.data.media :as di]
|
||||
[app.main.data.messages :as dm]
|
||||
[app.main.data.workspace.common :as dwc]
|
||||
[app.main.data.workspace.changes :as dch]
|
||||
[app.main.data.workspace.libraries :as dwl]
|
||||
[app.main.data.workspace.selection :as dws]
|
||||
[app.main.data.workspace.svg-upload :as svg]
|
||||
|
@ -51,7 +52,7 @@
|
|||
(let [stoper (rx/filter #(= ::finalize %) stream)
|
||||
forcer (rx/filter #(= ::force-persist %) stream)
|
||||
notifier (->> stream
|
||||
(rx/filter (ptk/type? ::dwc/commit-changes))
|
||||
(rx/filter dch/commit-changes?)
|
||||
(rx/debounce 2000)
|
||||
(rx/merge stoper forcer))
|
||||
|
||||
|
@ -79,7 +80,7 @@
|
|||
(st/emit! (update-persistence-status {:status :saved})))]
|
||||
(->> (rx/merge
|
||||
(->> stream
|
||||
(rx/filter (ptk/type? ::dwc/commit-changes))
|
||||
(rx/filter dch/commit-changes?)
|
||||
(rx/map deref)
|
||||
(rx/filter local-file?)
|
||||
(rx/tap on-dirty)
|
||||
|
@ -91,7 +92,7 @@
|
|||
(rx/tap on-saving)
|
||||
(rx/take-until (rx/delay 100 stoper)))
|
||||
(->> stream
|
||||
(rx/filter (ptk/type? ::dwc/commit-changes))
|
||||
(rx/filter dch/commit-changes?)
|
||||
(rx/map deref)
|
||||
(rx/filter library-file?)
|
||||
(rx/filter (complement #(empty? (:changes %))))
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
[app.common.spec :as us]
|
||||
[app.common.uuid :as uuid]
|
||||
[app.main.data.workspace.common :as dwc]
|
||||
[app.main.data.workspace.changes :as dch]
|
||||
[app.main.data.modal :as md]
|
||||
[app.main.streams :as ms]
|
||||
[app.main.worker :as uw]))
|
||||
|
@ -395,7 +396,7 @@
|
|||
(map #(get-in % [:obj :id]))
|
||||
(into (d/ordered-set)))]
|
||||
|
||||
(rx/of (dwc/commit-changes rchanges uchanges {:commit-local? true})
|
||||
(rx/of (dch/commit-changes rchanges uchanges {:commit-local? true})
|
||||
(select-shapes selected))))))
|
||||
|
||||
(defn change-hover-state
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
[app.main.data.workspace :as dw]
|
||||
[app.main.data.workspace.colors :as mdc]
|
||||
[app.main.data.workspace.common :as dwc]
|
||||
[app.main.data.workspace.undo :as dwu]
|
||||
[app.main.data.workspace.drawing :as dwd]
|
||||
[app.main.data.workspace.libraries :as dwl]
|
||||
[app.main.data.workspace.texts :as dwtxt]
|
||||
|
@ -123,7 +124,7 @@
|
|||
|
||||
:clear-undo {:tooltip (ds/meta "Q")
|
||||
:command (ds/c-mod "q")
|
||||
:fn #(st/emit! dwc/reinitialize-undo)}
|
||||
:fn #(st/emit! dwu/reinitialize-undo)}
|
||||
|
||||
:draw-frame {:tooltip "A"
|
||||
:command "a"
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
[app.common.pages :as cp]
|
||||
[app.common.uuid :as uuid]
|
||||
[app.main.data.workspace.common :as dwc]
|
||||
[app.main.data.workspace.changes :as dch]
|
||||
[app.main.repo :as rp]
|
||||
[app.util.color :as uc]
|
||||
[app.util.path.parser :as upp]
|
||||
|
@ -462,7 +463,7 @@
|
|||
|
||||
rchanges (conj rchanges reg-objects-action)]
|
||||
|
||||
(rx/of (dwc/commit-changes rchanges uchanges {:commit-local? true})
|
||||
(rx/of (dch/commit-changes rchanges uchanges {:commit-local? true})
|
||||
(dwc/select-shapes (d/ordered-set root-id))))
|
||||
|
||||
(catch :default e
|
||||
|
|
|
@ -15,6 +15,8 @@
|
|||
[app.main.data.workspace.selection :as dws]
|
||||
[app.main.data.workspace.common :as dwc]
|
||||
[app.main.data.workspace.transforms :as dwt]
|
||||
[app.main.data.workspace.changes :as dch]
|
||||
[app.main.data.workspace.undo :as dwu]
|
||||
[app.main.fonts :as fonts]
|
||||
[app.util.object :as obj]
|
||||
[app.util.text-editor :as ted]
|
||||
|
@ -77,8 +79,8 @@
|
|||
(when (and (not= content (:content shape))
|
||||
(some? (:current-page-id state)))
|
||||
(rx/of
|
||||
(dwc/update-shapes [id] #(assoc % :content content))
|
||||
(dwc/commit-undo-transaction)))))
|
||||
(dch/update-shapes [id] #(assoc % :content content))
|
||||
(dwu/commit-undo-transaction)))))
|
||||
(rx/of (dws/deselect-shape id)
|
||||
(dwc/delete-shapes [id])))))))
|
||||
|
||||
|
@ -141,7 +143,7 @@
|
|||
shape-ids (cond (= (:type shape) :text) [id]
|
||||
(= (:type shape) :group) (cp/get-children id objects))]
|
||||
|
||||
(rx/of (dwc/update-shapes shape-ids update-fn))))))
|
||||
(rx/of (dch/update-shapes shape-ids update-fn))))))
|
||||
|
||||
(defn update-paragraph-attrs
|
||||
[{:keys [id attrs]}]
|
||||
|
@ -169,7 +171,7 @@
|
|||
shape-ids (cond (= (:type shape) :text) [id]
|
||||
(= (:type shape) :group) (cp/get-children id objects))]
|
||||
|
||||
(rx/of (dwc/update-shapes shape-ids update-fn))))))))
|
||||
(rx/of (dch/update-shapes shape-ids update-fn))))))))
|
||||
|
||||
(defn update-text-attrs
|
||||
[{:keys [id attrs]}]
|
||||
|
@ -187,7 +189,7 @@
|
|||
update-fn #(update-shape % txt/is-text-node? attrs/merge attrs)
|
||||
shape-ids (cond (= (:type shape) :text) [id]
|
||||
(= (:type shape) :group) (cp/get-children id objects))]
|
||||
(rx/of (dwc/update-shapes shape-ids update-fn)))))))
|
||||
(rx/of (dch/update-shapes shape-ids update-fn)))))))
|
||||
|
||||
;; --- RESIZE UTILS
|
||||
|
||||
|
@ -218,58 +220,41 @@
|
|||
ptk/WatchEvent
|
||||
(watch [_ state stream]
|
||||
(let [page-id (:current-page-id state)
|
||||
|
||||
objects0 (get-in state [:workspace-file :data :pages-index page-id :objects])
|
||||
objects1 (get-in state [:workspace-data :pages-index page-id :objects])]
|
||||
(if-not (every? #(contains? objects1(first %)) changes)
|
||||
objects (get-in state [:workspace-data :pages-index page-id :objects])]
|
||||
(if-not (every? #(contains? objects(first %)) changes)
|
||||
(rx/empty)
|
||||
(let [change-text-shape
|
||||
(fn [objects [id [new-width new-height]]]
|
||||
(when (contains? objects id)
|
||||
(let [shape (get objects id)
|
||||
{:keys [selrect grow-type overflow-text]} (gsh/transform-shape shape)
|
||||
{shape-width :width shape-height :height} selrect
|
||||
|
||||
modifier-width (gsh/resize-modifiers shape :width new-width)
|
||||
modifier-height (gsh/resize-modifiers shape :height new-height)
|
||||
(let [changes-map (->> changes (into {}))
|
||||
ids (keys changes-map)
|
||||
update-fn
|
||||
(fn [shape]
|
||||
(let [[new-width new-height] (get changes-map (:id shape))
|
||||
{:keys [selrect grow-type overflow-text]} (gsh/transform-shape shape)
|
||||
{shape-width :width shape-height :height} selrect
|
||||
|
||||
shape (cond-> shape
|
||||
(and overflow-text (not= :fixed grow-type))
|
||||
(assoc :overflow-text false)
|
||||
modifier-width (gsh/resize-modifiers shape :width new-width)
|
||||
modifier-height (gsh/resize-modifiers shape :height new-height)]
|
||||
|
||||
(and (= :fixed grow-type) (not overflow-text) (> new-height shape-height))
|
||||
(assoc :overflow-text true)
|
||||
(cond-> shape
|
||||
(and overflow-text (not= :fixed grow-type))
|
||||
(assoc :overflow-text false)
|
||||
|
||||
(and (= :fixed grow-type) overflow-text (<= new-height shape-height))
|
||||
(assoc :overflow-text false)
|
||||
(and (= :fixed grow-type) (not overflow-text) (> new-height shape-height))
|
||||
(assoc :overflow-text true)
|
||||
|
||||
(and (not-changed? shape-width new-width) (= grow-type :auto-width))
|
||||
(-> (assoc :modifiers modifier-width)
|
||||
(gsh/transform-shape))
|
||||
(and (= :fixed grow-type) overflow-text (<= new-height shape-height))
|
||||
(assoc :overflow-text false)
|
||||
|
||||
(and (not-changed? shape-height new-height)
|
||||
(or (= grow-type :auto-height) (= grow-type :auto-width)))
|
||||
(-> (assoc :modifiers modifier-height)
|
||||
(gsh/transform-shape)))]
|
||||
(assoc objects id shape))))
|
||||
(and (not-changed? shape-width new-width) (= grow-type :auto-width))
|
||||
(-> (assoc :modifiers modifier-width)
|
||||
(gsh/transform-shape))
|
||||
|
||||
undo-transaction (get-in state [:workspace-undo :transaction])
|
||||
objects2 (->> changes (reduce change-text-shape objects1))
|
||||
(and (not-changed? shape-height new-height)
|
||||
(or (= grow-type :auto-height) (= grow-type :auto-width)))
|
||||
(-> (assoc :modifiers modifier-height)
|
||||
(gsh/transform-shape)))))]
|
||||
|
||||
regchg {:type :reg-objects
|
||||
:page-id page-id
|
||||
:shapes (vec (keys changes))}
|
||||
|
||||
rchanges (dwc/generate-changes page-id objects1 objects2)
|
||||
uchanges (dwc/generate-changes page-id objects2 objects0)]
|
||||
|
||||
(if (seq rchanges)
|
||||
(rx/concat
|
||||
(when-not undo-transaction
|
||||
(rx/of (dwc/start-undo-transaction)))
|
||||
(rx/of (dwc/commit-changes (conj rchanges regchg) (conj uchanges regchg) {:commit-local? true}))
|
||||
(when-not undo-transaction
|
||||
(rx/of (dwc/discard-undo-transaction)))))))))))
|
||||
(rx/of (dch/update-shapes ids update-fn {:reg-objects? true}))))))))
|
||||
|
||||
;; When a resize-event arrives we start "buffering" for a time
|
||||
;; after that time we invoke `resize-text-batch` with all the changes
|
||||
|
|
|
@ -14,7 +14,9 @@
|
|||
[app.common.pages :as cp]
|
||||
[app.common.spec :as us]
|
||||
[app.main.data.workspace.common :as dwc]
|
||||
[app.main.data.workspace.changes :as dch]
|
||||
[app.main.data.workspace.selection :as dws]
|
||||
[app.main.data.workspace.undo :as dwu]
|
||||
[app.main.refs :as refs]
|
||||
[app.main.snap :as snap]
|
||||
[app.main.store :as st]
|
||||
|
@ -140,7 +142,7 @@
|
|||
(filter #(= :text (:type %)))
|
||||
(map :id))]
|
||||
(rx/concat
|
||||
(rx/of (dwc/update-shapes text-shapes-ids #(assoc % :grow-type :fixed)))
|
||||
(rx/of (dch/update-shapes text-shapes-ids #(assoc % :grow-type :fixed)))
|
||||
(->> ms/mouse-position
|
||||
(rx/with-latest vector ms/mouse-position-shift)
|
||||
(rx/map normalize-proportion-lock)
|
||||
|
@ -259,9 +261,9 @@
|
|||
:shapes [(:id shape)]})))]
|
||||
|
||||
(when-not (empty? rch)
|
||||
(rx/of dwc/pop-undo-into-transaction
|
||||
(dwc/commit-changes rch uch {:commit-local? true})
|
||||
(dwc/commit-undo-transaction)
|
||||
(rx/of dwu/pop-undo-into-transaction
|
||||
(dch/commit-changes rch uch {:commit-local? true})
|
||||
(dwu/commit-undo-transaction)
|
||||
(dwc/expand-collapse frame-id)))))))
|
||||
|
||||
(defn start-move
|
||||
|
@ -452,35 +454,13 @@
|
|||
(ptk/reify ::apply-modifiers
|
||||
ptk/WatchEvent
|
||||
(watch [_ state stream]
|
||||
(let [page-id (:current-page-id state)
|
||||
|
||||
objects0 (get-in state [:workspace-file :data :pages-index page-id :objects])
|
||||
objects1 (get-in state [:workspace-data :pages-index page-id :objects])
|
||||
|
||||
;; ID's + Children ID's
|
||||
ids-with-children (d/concat [] (mapcat #(cp/get-children % objects1) ids) ids)
|
||||
|
||||
;; For each shape applies the modifiers by transforming the objects
|
||||
update-shape #(update %1 %2 gsh/transform-shape)
|
||||
objects2 (reduce update-shape objects1 ids-with-children)
|
||||
|
||||
regchg {:type :reg-objects
|
||||
:page-id page-id
|
||||
:shapes (vec ids)}
|
||||
|
||||
;; we need to generate redo chages from current
|
||||
;; state (with current temporal values) to new state but
|
||||
;; the undo should be calculated from clear current
|
||||
;; state (without temporal values in it, for this reason
|
||||
;; we have 3 different objects references).
|
||||
|
||||
rchanges (conj (dwc/generate-changes page-id objects1 objects2) regchg)
|
||||
uchanges (conj (dwc/generate-changes page-id objects2 objects0) regchg)]
|
||||
|
||||
(rx/of (dwc/start-undo-transaction)
|
||||
(dwc/commit-changes rchanges uchanges {:commit-local? true})
|
||||
(let [objects (dwc/lookup-page-objects state)
|
||||
children-ids (->> ids (mapcat #(cp/get-children % objects)))
|
||||
ids-with-children (d/concat [] children-ids ids)]
|
||||
(rx/of (dwu/start-undo-transaction)
|
||||
(dch/update-shapes ids-with-children gsh/transform-shape {:reg-objects? true})
|
||||
(clear-local-transform)
|
||||
(dwc/commit-undo-transaction))))))
|
||||
(dwu/commit-undo-transaction))))))
|
||||
|
||||
;; --- Update Dimensions
|
||||
|
||||
|
|
134
frontend/src/app/main/data/workspace/undo.cljs
Normal file
134
frontend/src/app/main/data/workspace/undo.cljs
Normal file
|
@ -0,0 +1,134 @@
|
|||
;; This Source Code Form is subject to the terms of the Mozilla Public
|
||||
;; License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
;; file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
;;
|
||||
;; Copyright (c) UXBOX Labs SL
|
||||
|
||||
(ns app.main.data.workspace.undo
|
||||
(:require
|
||||
[app.common.data :as d]
|
||||
[app.common.geom.proportions :as gpr]
|
||||
[app.common.geom.shapes :as gsh]
|
||||
[app.common.pages :as cp]
|
||||
[app.common.pages.spec :as spec]
|
||||
[app.common.spec :as us]
|
||||
[app.common.uuid :as uuid]
|
||||
[app.main.worker :as uw]
|
||||
[app.main.streams :as ms]
|
||||
[app.util.logging :as log]
|
||||
[app.util.timers :as ts]
|
||||
[beicon.core :as rx]
|
||||
[cljs.spec.alpha :as s]
|
||||
[clojure.set :as set]
|
||||
[cuerdas.core :as str]
|
||||
[potok.core :as ptk]))
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;; Undo / Redo
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
||||
(s/def ::undo-changes ::cp/changes)
|
||||
(s/def ::redo-changes ::cp/changes)
|
||||
(s/def ::undo-entry
|
||||
(s/keys :req-un [::undo-changes ::redo-changes]))
|
||||
|
||||
(def MAX-UNDO-SIZE 50)
|
||||
|
||||
(defn- conj-undo-entry
|
||||
[undo data]
|
||||
(let [undo (conj undo data)
|
||||
cnt (count undo)]
|
||||
(if (> cnt MAX-UNDO-SIZE)
|
||||
(subvec undo (- cnt MAX-UNDO-SIZE))
|
||||
undo)))
|
||||
|
||||
(defn- materialize-undo
|
||||
[changes index]
|
||||
(ptk/reify ::materialize-undo
|
||||
ptk/UpdateEvent
|
||||
(update [_ state]
|
||||
(-> state
|
||||
(update :workspace-data cp/process-changes changes)
|
||||
(assoc-in [:workspace-undo :index] index)))))
|
||||
|
||||
(defn- reset-undo
|
||||
[index]
|
||||
(ptk/reify ::reset-undo
|
||||
ptk/UpdateEvent
|
||||
(update [_ state]
|
||||
(-> state
|
||||
(update :workspace-undo dissoc :undo-index)
|
||||
(update-in [:workspace-undo :items] (fn [queue] (into [] (take (inc index) queue))))))))
|
||||
|
||||
(defn- add-undo-entry
|
||||
[state entry]
|
||||
(if (and entry
|
||||
(not-empty (:undo-changes entry))
|
||||
(not-empty (:redo-changes entry)))
|
||||
(let [index (get-in state [:workspace-undo :index] -1)
|
||||
items (get-in state [:workspace-undo :items] [])
|
||||
items (->> items (take (inc index)) (into []))
|
||||
items (conj-undo-entry items entry)]
|
||||
(-> state
|
||||
(update :workspace-undo assoc :items items
|
||||
:index (min (inc index)
|
||||
(dec MAX-UNDO-SIZE)))))
|
||||
state))
|
||||
|
||||
(defn- accumulate-undo-entry
|
||||
[state {:keys [undo-changes redo-changes]}]
|
||||
(-> state
|
||||
(update-in [:workspace-undo :transaction :undo-changes] #(into undo-changes %))
|
||||
(update-in [:workspace-undo :transaction :redo-changes] #(into % redo-changes))))
|
||||
|
||||
(defn- append-undo
|
||||
[entry]
|
||||
(us/assert ::undo-entry entry)
|
||||
(ptk/reify ::append-undo
|
||||
ptk/UpdateEvent
|
||||
(update [_ state]
|
||||
(if (get-in state [:workspace-undo :transaction])
|
||||
(accumulate-undo-entry state entry)
|
||||
(add-undo-entry state entry)))))
|
||||
|
||||
(defonce empty-tx {:undo-changes [] :redo-changes []})
|
||||
|
||||
(defn start-undo-transaction []
|
||||
(ptk/reify ::start-undo-transaction
|
||||
ptk/UpdateEvent
|
||||
(update [_ state]
|
||||
;; We commit the old transaction before starting the new one
|
||||
(let [current-tx (get-in state [:workspace-undo :transaction])]
|
||||
(cond-> state
|
||||
(nil? current-tx) (assoc-in [:workspace-undo :transaction] empty-tx))))))
|
||||
|
||||
(defn discard-undo-transaction []
|
||||
(ptk/reify ::discard-undo-transaction
|
||||
ptk/UpdateEvent
|
||||
(update [_ state]
|
||||
(update state :workspace-undo dissoc :transaction))))
|
||||
|
||||
(defn commit-undo-transaction []
|
||||
(ptk/reify ::commit-undo-transaction
|
||||
ptk/UpdateEvent
|
||||
(update [_ state]
|
||||
(-> state
|
||||
(add-undo-entry (get-in state [:workspace-undo :transaction]))
|
||||
(update :workspace-undo dissoc :transaction)))))
|
||||
|
||||
(def pop-undo-into-transaction
|
||||
(ptk/reify ::last-undo-into-transaction
|
||||
ptk/UpdateEvent
|
||||
(update [_ state]
|
||||
(let [index (get-in state [:workspace-undo :index] -1)]
|
||||
|
||||
(cond-> state
|
||||
(>= index 0) (accumulate-undo-entry (get-in state [:workspace-undo :items index]))
|
||||
(>= index 0) (update-in [:workspace-undo :index] dec))))))
|
||||
|
||||
(def reinitialize-undo
|
||||
(ptk/reify ::reset-undo
|
||||
ptk/UpdateEvent
|
||||
(update [_ state]
|
||||
(assoc state :workspace-undo {}))))
|
||||
|
|
@ -11,8 +11,8 @@
|
|||
[app.main.store :as st]
|
||||
[app.main.refs :as refs]
|
||||
[app.common.geom.point :as gpt]
|
||||
[app.util.globals :as globals])
|
||||
(:import goog.events.KeyCodes))
|
||||
[app.util.globals :as globals]
|
||||
[app.util.keyboard :as kbd]))
|
||||
|
||||
;; --- User Events
|
||||
|
||||
|
@ -113,8 +113,7 @@
|
|||
ob (->> (rx/merge
|
||||
(->> st/stream
|
||||
(rx/filter keyboard-event?)
|
||||
(rx/filter #(let [key (:key %)]
|
||||
(= key KeyCodes.ALT)))
|
||||
(rx/filter kbd/altKey?)
|
||||
(rx/map #(= :down (:type %))))
|
||||
;; Fix a situation caused by using `ctrl+alt` kind of shortcuts,
|
||||
;; that makes keyboard-alt stream registring the key pressed but
|
||||
|
@ -130,10 +129,7 @@
|
|||
ob (->> (rx/merge
|
||||
(->> st/stream
|
||||
(rx/filter keyboard-event?)
|
||||
(rx/filter #(let [key (:key %)]
|
||||
(or
|
||||
(= key KeyCodes.CTRL)
|
||||
(= key KeyCodes.META))))
|
||||
(rx/filter kbd/ctrlKey?)
|
||||
(rx/map #(= :down (:type %))))
|
||||
;; Fix a situation caused by using `ctrl+alt` kind of shortcuts,
|
||||
;; that makes keyboard-alt stream registring the key pressed but
|
||||
|
|
|
@ -46,7 +46,7 @@
|
|||
(mf/use-callback
|
||||
(mf/deps top (:offset @local))
|
||||
(fn [node]
|
||||
(when (and node (not fixed?))
|
||||
(when (some? node)
|
||||
(let [{node-height :height} (dom/get-bounding-rect node)
|
||||
{window-height :height} (dom/get-window-size)
|
||||
target-offset (if (> (+ top node-height) window-height)
|
||||
|
|
|
@ -3,10 +3,10 @@
|
|||
[rumext.alpha :as mf]
|
||||
[app.common.uuid :as uuid]
|
||||
[app.util.dom :as dom]
|
||||
[app.util.keyboard :as kbd]
|
||||
[goog.events :as events]
|
||||
[goog.object :as gobj])
|
||||
(:import goog.events.EventType
|
||||
goog.events.KeyCodes))
|
||||
(:import goog.events.EventType))
|
||||
|
||||
(mf/defc dropdown'
|
||||
{::mf/wrap-props false}
|
||||
|
@ -27,7 +27,7 @@
|
|||
|
||||
on-keyup
|
||||
(fn [event]
|
||||
(when (= (.-keyCode event) 27) ; ESC
|
||||
(when (kbd/esc? event)
|
||||
(on-close)))
|
||||
|
||||
on-mount
|
||||
|
|
|
@ -200,7 +200,7 @@
|
|||
(when selected?
|
||||
[:& file-menu {:files selected-file-objs
|
||||
:show? (:menu-open @local)
|
||||
:left (:x (:menu-pos @local))
|
||||
:left (+ 24 (:x (:menu-pos @local)))
|
||||
:top (:y (:menu-pos @local))
|
||||
:navigate? navigate?
|
||||
:on-edit on-edit
|
||||
|
|
|
@ -31,9 +31,10 @@
|
|||
(if (:is-default team)
|
||||
(t locale "dashboard.your-penpot")
|
||||
(:name team))))
|
||||
(st/emit! (dd/search-files {:team-id (:id team)
|
||||
:search-term search-term})
|
||||
(dd/clear-selected-files))))
|
||||
(when search-term
|
||||
(st/emit! (dd/search-files {:team-id (:id team)
|
||||
:search-term search-term})
|
||||
(dd/clear-selected-files)))))
|
||||
|
||||
[:*
|
||||
[:header.dashboard-header
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
[app.main.data.dashboard :as dd]
|
||||
[app.main.data.messages :as dm]
|
||||
[app.main.data.modal :as modal]
|
||||
[app.main.data.users :as du]
|
||||
[app.main.refs :as refs]
|
||||
[app.main.repo :as rp]
|
||||
[app.main.store :as st]
|
||||
|
@ -241,6 +242,8 @@
|
|||
::mf/register-as ::leave-and-reassign}
|
||||
[{:keys [members profile team accept]}]
|
||||
(let [form (fm/use-form :spec ::leave-modal-form :initial {})
|
||||
not-current-user? (fn [{:keys [id]}] (not= id (:id profile)))
|
||||
members (->> members (filterv not-current-user?))
|
||||
options (into [{:value "" :label (tr "modals.leave-and-reassign.select-memeber-to-promote")}]
|
||||
(map #(hash-map :label (:name %) :value (str (:id %))) members))
|
||||
|
||||
|
@ -264,11 +267,14 @@
|
|||
|
||||
[:div.modal-content.generic-form
|
||||
[:p (tr "modals.leave-and-reassign.hint1" (:name team))]
|
||||
[:p (tr "modals.leave-and-reassign.hint2")]
|
||||
|
||||
[:& fm/form {:form form}
|
||||
[:& fm/select {:name :member-id
|
||||
:options options}]]]
|
||||
(if (empty? members)
|
||||
[:p (tr "modals.leave-and-reassign.forbiden")]
|
||||
[:*
|
||||
[:p (tr "modals.leave-and-reassign.hint2")]
|
||||
[:& fm/form {:form form}
|
||||
[:& fm/select {:name :member-id
|
||||
:options options}]]])]
|
||||
|
||||
[:div.modal-footer
|
||||
[:div.action-buttons
|
||||
|
@ -279,7 +285,7 @@
|
|||
|
||||
[:input.accept-button
|
||||
{:type "button"
|
||||
:class (when-not (:valid @form) "btn-disabled")
|
||||
:class (if (:valid @form) "primary" "btn-disabled")
|
||||
:disabled (not (:valid @form))
|
||||
:value (tr "modals.leave-and-reassign.promote-and-leave")
|
||||
:on-click on-accept}]]]]]))
|
||||
|
@ -314,7 +320,9 @@
|
|||
(fn []
|
||||
(let [team-id (:default-team-id profile)]
|
||||
(da/set-current-team! team-id)
|
||||
(st/emit! (rt/nav :dashboard-projects {:team-id team-id})))))
|
||||
(st/emit! (modal/hide)
|
||||
(du/fetch-teams)
|
||||
(rt/nav :dashboard-projects {:team-id team-id})))))
|
||||
|
||||
leave-fn
|
||||
(mf/use-callback
|
||||
|
|
|
@ -303,7 +303,7 @@
|
|||
(if (:is-default team)
|
||||
(tr "dashboard.your-penpot")
|
||||
(:name team))))
|
||||
(st/emitf (dd/fetch-team-members team)
|
||||
(st/emit! (dd/fetch-team-members team)
|
||||
(dd/fetch-team-stats team))))
|
||||
|
||||
[:*
|
||||
|
|
|
@ -121,10 +121,7 @@
|
|||
(defn add-layer-props [attrs shape]
|
||||
(cond-> attrs
|
||||
(:opacity shape)
|
||||
(obj/set! "opacity" (:opacity shape))
|
||||
|
||||
(and (:blend-mode shape) (not= (:blend-mode shape) :normal))
|
||||
(obj/set! "mixBlendMode" (d/name (:blend-mode shape)))))
|
||||
(obj/set! "opacity" (:opacity shape))))
|
||||
|
||||
(defn extract-svg-attrs
|
||||
[render-id svg-defs svg-attrs]
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
|
||||
(ns app.main.ui.shapes.shape
|
||||
(:require
|
||||
[app.common.data :as d]
|
||||
[app.common.uuid :as uuid]
|
||||
[app.main.ui.context :as muc]
|
||||
[app.main.ui.shapes.filters :as filters]
|
||||
|
@ -23,7 +24,10 @@
|
|||
render-id (mf/use-memo #(str (uuid/next)))
|
||||
filter-id (str "filter_" render-id)
|
||||
styles (-> (obj/new)
|
||||
(obj/set! "pointerEvents" pointer-events))
|
||||
(obj/set! "pointerEvents" pointer-events)
|
||||
|
||||
(cond-> (and (:blend-mode shape) (not= (:blend-mode shape) :normal))
|
||||
(obj/set! "mixBlendMode" (d/name (:blend-mode shape)))))
|
||||
|
||||
{:keys [x y width height type]} shape
|
||||
frame? (= :frame type)
|
||||
|
|
|
@ -65,15 +65,15 @@
|
|||
:frame frame
|
||||
:childs childs
|
||||
:is-child-selected? true}]
|
||||
(when (and (:interactions shape) show-interactions?)
|
||||
(when (:interactions shape)
|
||||
[:rect {:x (- x 1)
|
||||
:y (- y 1)
|
||||
:width (+ width 2)
|
||||
:height (+ height 2)
|
||||
:fill "#31EFB8"
|
||||
:stroke "#31EFB8"
|
||||
:stroke-width 1
|
||||
:fill-opacity 0.2}])]
|
||||
:stroke-width (if show-interactions? 1 0)
|
||||
:fill-opacity (if show-interactions? 0.2 0)}])]
|
||||
|
||||
;; Don't wrap svg elements inside a <g> otherwise some can break
|
||||
[:& component {:shape shape
|
||||
|
|
|
@ -24,8 +24,7 @@
|
|||
[app.common.math :as mth]
|
||||
[app.util.router :as rt]
|
||||
[app.main.data.viewer :as vd])
|
||||
(:import goog.events.EventType
|
||||
goog.events.KeyCodes))
|
||||
(:import goog.events.EventType))
|
||||
|
||||
(mf/defc thumbnails-content
|
||||
[{:keys [children expanded? total] :as props}]
|
||||
|
|
|
@ -164,8 +164,12 @@
|
|||
|
||||
on-select-library-color
|
||||
(fn [color]
|
||||
(reset! state (data->state color))
|
||||
(on-change color))
|
||||
(let [editing-stop (:editing-stop @state)
|
||||
is-gradient? (some? (:gradient color))]
|
||||
(if (and (some? editing-stop) (not is-gradient?))
|
||||
(handle-change-color (color->components (:color color) (:opacity color)))
|
||||
(do (reset! state (data->state color))
|
||||
(on-change color)))))
|
||||
|
||||
on-add-library-color
|
||||
(fn [color]
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
[app.main.data.modal :as modal]
|
||||
[app.main.data.workspace :as dw]
|
||||
[app.main.data.workspace.common :as dwc]
|
||||
[app.main.data.workspace.undo :as dwu]
|
||||
[app.main.data.workspace.libraries :as dwl]
|
||||
[app.main.data.workspace.shortcuts :as sc]
|
||||
[app.main.refs :as refs]
|
||||
|
@ -83,10 +84,10 @@
|
|||
;; We defer the execution so the mouse event won't close the editor
|
||||
(timers/schedule #(st/emit! (dw/start-editing-selected))))
|
||||
do-update-component (st/emitf
|
||||
(dwc/start-undo-transaction)
|
||||
(dwu/start-undo-transaction)
|
||||
(dwl/update-component id)
|
||||
(dwl/sync-file current-file-id (:component-file shape))
|
||||
(dwc/commit-undo-transaction))
|
||||
(dwu/commit-undo-transaction))
|
||||
confirm-update-remote-component (st/emitf
|
||||
(dwl/update-component id)
|
||||
(dwl/sync-file current-file-id
|
||||
|
|
|
@ -12,7 +12,8 @@
|
|||
(defn generic-wrapper-factory
|
||||
[component]
|
||||
(mf/fnc generic-wrapper
|
||||
{::mf/wrap-props false}
|
||||
{::mf/wrap [#(mf/memo' % (mf/check-props ["shape"]))]
|
||||
::mf/wrap-props false}
|
||||
[props]
|
||||
(let [shape (unchecked-get props "shape")]
|
||||
[:> shape-container {:shape shape}
|
||||
|
|
|
@ -28,6 +28,28 @@
|
|||
(contains? (:selected local) id)))]
|
||||
(l/derived check-moving refs/workspace-local)))
|
||||
|
||||
(defn check-props
|
||||
([props] (check-props props =))
|
||||
([props eqfn?]
|
||||
(fn [np op]
|
||||
(every? #(eqfn? (unchecked-get np %)
|
||||
(unchecked-get op %))
|
||||
props))))
|
||||
|
||||
(defn check-frame-props
|
||||
"Checks for changes in the props of a frame"
|
||||
[new-props old-props]
|
||||
(let [new-shape (unchecked-get new-props "shape")
|
||||
old-shape (unchecked-get old-props "shape")
|
||||
|
||||
new-objects (unchecked-get new-props "objects")
|
||||
old-objects (unchecked-get old-props "objects")
|
||||
|
||||
new-children (->> new-shape :shapes (mapv #(get new-objects %)))
|
||||
old-children (->> old-shape :shapes (mapv #(get old-objects %)))]
|
||||
(and (= new-shape old-shape)
|
||||
(= new-children old-children))))
|
||||
|
||||
;; This custom deffered don't deffer rendering when ghost rendering is
|
||||
;; used.
|
||||
(defn custom-deferred
|
||||
|
@ -48,7 +70,7 @@
|
|||
[shape-wrapper]
|
||||
(let [frame-shape (frame/frame-shape shape-wrapper)]
|
||||
(mf/fnc frame-wrapper
|
||||
{::mf/wrap [#(mf/memo' % (mf/check-props ["shape" "objects"])) custom-deferred]
|
||||
{::mf/wrap [#(mf/memo' % check-frame-props) custom-deferred]
|
||||
::mf/wrap-props false}
|
||||
[props]
|
||||
(let [shape (unchecked-get props "shape")
|
||||
|
|
|
@ -30,7 +30,7 @@
|
|||
[shape-wrapper]
|
||||
(let [group-shape (group/group-shape shape-wrapper)]
|
||||
(mf/fnc group-wrapper
|
||||
{::mf/wrap [#(mf/memo' % (mf/check-props ["shape" "frame"]))]
|
||||
{::mf/wrap [#(mf/memo' % (mf/check-props ["shape"]))]
|
||||
::mf/wrap-props false}
|
||||
[props]
|
||||
(let [shape (unchecked-get props "shape")
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
[app.main.ui.cursors :as cur]
|
||||
[app.main.ui.shapes.text.styles :as sts]
|
||||
[app.util.dom :as dom]
|
||||
[app.util.keyboard :as kbd]
|
||||
[app.util.object :as obj]
|
||||
[app.util.text-editor :as ted]
|
||||
[cuerdas.core :as str]
|
||||
|
@ -25,8 +26,7 @@
|
|||
[okulary.core :as l]
|
||||
[rumext.alpha :as mf])
|
||||
(:import
|
||||
goog.events.EventType
|
||||
goog.events.KeyCodes))
|
||||
goog.events.EventType))
|
||||
|
||||
;; --- Text Editor Rendering
|
||||
|
||||
|
@ -84,7 +84,7 @@
|
|||
on-key-up
|
||||
(fn [event]
|
||||
(dom/stop-propagation event)
|
||||
(when (= (.-keyCode event) 27) ; ESC
|
||||
(when (kbd/esc? event)
|
||||
(do
|
||||
(st/emit! :interrupt)
|
||||
(st/emit! dw/clear-edition-mode))))
|
||||
|
@ -163,10 +163,18 @@
|
|||
::mf/wrap-props false
|
||||
::mf/forward-ref true}
|
||||
[props ref]
|
||||
(let [{:keys [id x y width height grow-type] :as shape} (obj/get props "shape")]
|
||||
[:foreignObject {:transform (gsh/transform-matrix shape)
|
||||
:x x :y y
|
||||
:width (if (#{:auto-width} grow-type) 100000 width)
|
||||
:height (if (#{:auto-height :auto-width} grow-type) 100000 height)}
|
||||
(let [{:keys [id x y width height grow-type] :as shape} (obj/get props "shape")
|
||||
clip-id (str "clip-" id)]
|
||||
[:g.text-editor {:clip-path (str "url(#" clip-id ")")}
|
||||
[:defs
|
||||
;; This clippath will cut the huge foreign object we use to calculate the automatic resize
|
||||
[:clipPath {:id clip-id}
|
||||
[:rect {:x x :y y
|
||||
:width (+ width 8) :height (+ height 8)
|
||||
:transform (gsh/transform-matrix shape)}]]]
|
||||
[:foreignObject {:transform (gsh/transform-matrix shape)
|
||||
:x x :y y
|
||||
:width (if (#{:auto-width} grow-type) 100000 width)
|
||||
:height (if (#{:auto-height :auto-width} grow-type) 100000 height)}
|
||||
|
||||
[:& text-shape-edit-html {:shape shape :key (str id)}]]))
|
||||
[:& text-shape-edit-html {:shape shape :key (str id)}]]]))
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
[app.main.data.modal :as modal]
|
||||
[app.main.data.workspace :as dw]
|
||||
[app.main.data.workspace.common :as dwc]
|
||||
[app.main.data.workspace.undo :as dwu]
|
||||
[app.main.data.workspace.libraries :as dwl]
|
||||
[app.main.data.workspace.texts :as dwt]
|
||||
[app.main.exports :as exports]
|
||||
|
@ -45,38 +46,6 @@
|
|||
[okulary.core :as l]
|
||||
[rumext.alpha :as mf]))
|
||||
|
||||
|
||||
;; ---- Assets selection management
|
||||
|
||||
(def empty-selection #{})
|
||||
|
||||
(defn toggle-select
|
||||
[selected asset-id]
|
||||
(if (contains? selected asset-id)
|
||||
(disj selected asset-id)
|
||||
(conj selected asset-id)))
|
||||
|
||||
(defn replace-select
|
||||
[selected asset-id]
|
||||
#{asset-id})
|
||||
|
||||
(defn extend-select
|
||||
[selected asset-id groups]
|
||||
(let [assets (->> groups vals flatten)
|
||||
clicked-idx (d/index-of-pred assets #(= (:id %) asset-id))
|
||||
selected-idx (->> selected
|
||||
(map (fn [id] (d/index-of-pred assets
|
||||
#(= (:id %) id)))))
|
||||
min-idx (apply min (conj selected-idx clicked-idx))
|
||||
max-idx (apply max (conj selected-idx clicked-idx))]
|
||||
|
||||
(->> assets
|
||||
d/enumerate
|
||||
(filter #(<= min-idx (first %) max-idx))
|
||||
(map #(-> % second :id))
|
||||
set)))
|
||||
|
||||
|
||||
;; ---- Group assets management ----
|
||||
|
||||
(s/def ::asset-name ::us/not-empty-string)
|
||||
|
@ -149,40 +118,42 @@
|
|||
;; ---- Components box ----
|
||||
|
||||
(mf/defc components-box
|
||||
[{:keys [file-id local? components listing-thumbs? open? change-selected] :as props}]
|
||||
[{:keys [file-id local? components listing-thumbs? open? selected-assets
|
||||
on-asset-click on-assets-delete on-clear-selection] :as props}]
|
||||
(let [state (mf/use-state {:menu-open false
|
||||
:renaming nil
|
||||
:top nil
|
||||
:left nil
|
||||
:component-id nil
|
||||
:selected empty-selection
|
||||
:folded-groups empty-folded-groups})
|
||||
|
||||
selected-components (:components selected-assets)
|
||||
multi-components? (> (count selected-components) 1)
|
||||
multi-assets? (or (not (empty? (:graphics selected-assets)))
|
||||
(not (empty? (:colors selected-assets)))
|
||||
(not (empty? (:typographies selected-assets))))
|
||||
|
||||
groups (group-assets components)
|
||||
selected (:selected @state)
|
||||
folded-groups (:folded-groups @state)
|
||||
|
||||
on-duplicate
|
||||
(mf/use-callback
|
||||
(mf/deps @state)
|
||||
(fn []
|
||||
(if (empty? selected)
|
||||
(if (empty? selected-components)
|
||||
(st/emit! (dwl/duplicate-component {:id (:component-id @state)}))
|
||||
(do
|
||||
(st/emit! (dwc/start-undo-transaction))
|
||||
(apply st/emit! (map #(dwl/duplicate-component {:id %}) selected))
|
||||
(st/emit! (dwc/commit-undo-transaction))))))
|
||||
(st/emit! (dwu/start-undo-transaction))
|
||||
(apply st/emit! (map #(dwl/duplicate-component {:id %}) selected-components))
|
||||
(st/emit! (dwu/commit-undo-transaction))))))
|
||||
|
||||
on-delete
|
||||
(mf/use-callback
|
||||
(mf/deps @state)
|
||||
(mf/deps @state file-id multi-components? multi-assets?)
|
||||
(fn []
|
||||
(if (empty? selected)
|
||||
(st/emit! (dwl/delete-component {:id (:component-id @state)}))
|
||||
(do
|
||||
(st/emit! (dwc/start-undo-transaction))
|
||||
(apply st/emit! (map #(dwl/delete-component {:id %}) selected))
|
||||
(st/emit! (dwc/commit-undo-transaction))))
|
||||
(if (or multi-components? multi-assets?)
|
||||
(on-assets-delete)
|
||||
(st/emit! (dwl/delete-component {:id (:component-id @state)})))
|
||||
(st/emit! (dwl/sync-file file-id file-id))))
|
||||
|
||||
on-rename
|
||||
|
@ -205,51 +176,35 @@
|
|||
|
||||
on-context-menu
|
||||
(mf/use-callback
|
||||
(fn [component-id]
|
||||
(fn [event]
|
||||
(when local?
|
||||
(let [pos (dom/get-client-position event)
|
||||
top (:y pos)
|
||||
left (- (:x pos) 20)]
|
||||
(dom/prevent-default event)
|
||||
(swap! state assoc :menu-open true
|
||||
:top top
|
||||
:left left
|
||||
:component-id component-id))))))
|
||||
|
||||
unselect-all
|
||||
(mf/use-callback
|
||||
(fn [event]
|
||||
(swap! state assoc :selected empty-selection)))
|
||||
|
||||
on-select
|
||||
(mf/use-callback
|
||||
(mf/deps selected-components on-clear-selection)
|
||||
(fn [component-id]
|
||||
(fn [event]
|
||||
(dom/stop-propagation event)
|
||||
(swap! state update :selected
|
||||
(fn [selected]
|
||||
(cond
|
||||
(kbd/ctrl? event)
|
||||
(toggle-select selected component-id)
|
||||
|
||||
(kbd/shift? event)
|
||||
(extend-select selected component-id groups)))))))
|
||||
(when local?
|
||||
(let [pos (dom/get-client-position event)
|
||||
top (:y pos)
|
||||
left (- (:x pos) 20)]
|
||||
(dom/prevent-default event)
|
||||
(when-not (contains? selected-components component-id)
|
||||
(on-clear-selection))
|
||||
(swap! state assoc :menu-open true
|
||||
:top top
|
||||
:left left
|
||||
:component-id component-id))))))
|
||||
|
||||
create-group
|
||||
(mf/use-callback
|
||||
(mf/deps components selected)
|
||||
(mf/deps components selected-components on-clear-selection)
|
||||
(fn [name]
|
||||
(swap! state assoc :selected empty-selection)
|
||||
(st/emit! (dwc/start-undo-transaction))
|
||||
(on-clear-selection)
|
||||
(st/emit! (dwu/start-undo-transaction))
|
||||
(apply st/emit!
|
||||
(->> components
|
||||
(filter #(contains? selected (:id %)))
|
||||
(filter #(contains? selected-components (:id %)))
|
||||
(map #(dwl/rename-component
|
||||
(:id %)
|
||||
(str name " / "
|
||||
(cp/merge-path-item (:path %) (:name %)))))))
|
||||
(st/emit! (dwc/commit-undo-transaction))))
|
||||
(st/emit! (dwu/commit-undo-transaction))))
|
||||
|
||||
on-fold-group
|
||||
(mf/use-callback
|
||||
|
@ -262,7 +217,7 @@
|
|||
|
||||
on-group
|
||||
(mf/use-callback
|
||||
(mf/deps components selected)
|
||||
(mf/deps components selected-components)
|
||||
(fn [event]
|
||||
(dom/stop-propagation event)
|
||||
(modal/show! :create-group-dialog {:create create-group})))
|
||||
|
@ -274,11 +229,7 @@
|
|||
:component component})
|
||||
(dnd/set-allowed-effect! event "move")))]
|
||||
|
||||
(mf/use-effect
|
||||
(mf/deps [change-selected selected])
|
||||
#(change-selected (count selected)))
|
||||
|
||||
[:div.asset-section {:on-click unselect-all}
|
||||
[:div.asset-section
|
||||
[:div.asset-title {:class (when (not open?) "closed")}
|
||||
[:span {:on-click (st/emitf (dwl/set-assets-box-open file-id :components (not open?)))}
|
||||
i/arrow-slide (tr "workspace.assets.components")]
|
||||
|
@ -308,11 +259,11 @@
|
|||
(let [renaming? (= (:renaming @state)(:id component))]
|
||||
[:div {:key (:id component)
|
||||
:class-name (dom/classnames
|
||||
:selected (contains? selected (:id component))
|
||||
:selected (contains? selected-components (:id component))
|
||||
:grid-cell @listing-thumbs?
|
||||
:enum-item (not @listing-thumbs?))
|
||||
:draggable true
|
||||
:on-click (on-select (:id component))
|
||||
:on-click #(on-asset-click % (:id component) groups nil)
|
||||
:on-context-menu (on-context-menu (:id component))
|
||||
:on-drag-start (partial on-drag-start component)}
|
||||
[:& exports/component-svg {:group (get-in component [:objects (:id component)])
|
||||
|
@ -340,28 +291,35 @@
|
|||
:on-close #(swap! state assoc :menu-open false)
|
||||
:top (:top @state)
|
||||
:left (:left @state)
|
||||
:options [(when (<= (count selected) 1)
|
||||
:options [(when-not (or multi-components? multi-assets?)
|
||||
[(tr "workspace.assets.rename") on-rename])
|
||||
[(tr "workspace.assets.duplicate") on-duplicate]
|
||||
(when-not multi-assets?
|
||||
[(tr "workspace.assets.duplicate") on-duplicate])
|
||||
[(tr "workspace.assets.delete") on-delete]
|
||||
[(tr "workspace.assets.group") on-group]]}])]))
|
||||
(when-not multi-assets?
|
||||
[(tr "workspace.assets.group") on-group])]}])]))
|
||||
|
||||
|
||||
;; ---- Graphics box ----
|
||||
|
||||
(mf/defc graphics-box
|
||||
[{:keys [file-id local? objects listing-thumbs? open? change-selected] :as props}]
|
||||
[{:keys [file-id local? objects listing-thumbs? open? selected-assets
|
||||
on-asset-click on-assets-delete on-clear-selection] :as props}]
|
||||
(let [input-ref (mf/use-ref nil)
|
||||
state (mf/use-state {:menu-open false
|
||||
:renaming nil
|
||||
:top nil
|
||||
:left nil
|
||||
:object-id nil
|
||||
:selected empty-selection
|
||||
:folded-groups empty-folded-groups})
|
||||
|
||||
selected-objects (:graphics selected-assets)
|
||||
multi-objects? (> (count selected-objects) 1)
|
||||
multi-assets? (or (not (empty? (:components selected-assets)))
|
||||
(not (empty? (:colors selected-assets)))
|
||||
(not (empty? (:typographies selected-assets))))
|
||||
|
||||
groups (group-assets objects)
|
||||
selected (:selected @state)
|
||||
folded-groups (:folded-groups @state)
|
||||
|
||||
add-graphic
|
||||
|
@ -370,7 +328,7 @@
|
|||
(st/emitf (dwl/set-assets-box-open file-id :graphics true))
|
||||
(dom/click (mf/ref-val input-ref))))
|
||||
|
||||
on-selected
|
||||
on-file-selected
|
||||
(mf/use-callback
|
||||
(mf/deps file-id)
|
||||
(fn [blobs]
|
||||
|
@ -380,14 +338,11 @@
|
|||
|
||||
on-delete
|
||||
(mf/use-callback
|
||||
(mf/deps @state)
|
||||
(mf/deps @state multi-objects? multi-assets?)
|
||||
(fn []
|
||||
(if (empty? selected)
|
||||
(st/emit! (dwl/delete-media {:id (:object-id @state)}))
|
||||
(do
|
||||
(st/emit! (dwc/start-undo-transaction))
|
||||
(apply st/emit! (map #(dwl/delete-media {:id %}) selected))
|
||||
(st/emit! (dwc/commit-undo-transaction))))))
|
||||
(if (or multi-objects? multi-assets?)
|
||||
(on-assets-delete)
|
||||
(st/emit! (dwl/delete-media {:id (:object-id @state)})))))
|
||||
|
||||
on-rename
|
||||
(mf/use-callback
|
||||
|
@ -409,51 +364,35 @@
|
|||
|
||||
on-context-menu
|
||||
(mf/use-callback
|
||||
(fn [object-id]
|
||||
(fn [event]
|
||||
(when local?
|
||||
(let [pos (dom/get-client-position event)
|
||||
top (:y pos)
|
||||
left (- (:x pos) 20)]
|
||||
(dom/prevent-default event)
|
||||
(swap! state assoc :menu-open true
|
||||
:top top
|
||||
:left left
|
||||
:object-id object-id))))))
|
||||
|
||||
unselect-all
|
||||
(mf/use-callback
|
||||
(fn [event]
|
||||
(swap! state assoc :selected empty-selection)))
|
||||
|
||||
on-select
|
||||
(mf/use-callback
|
||||
(mf/deps selected-objects on-clear-selection)
|
||||
(fn [object-id]
|
||||
(fn [event]
|
||||
(dom/stop-propagation event)
|
||||
(swap! state update :selected
|
||||
(fn [selected]
|
||||
(cond
|
||||
(kbd/ctrl? event)
|
||||
(toggle-select selected object-id)
|
||||
|
||||
(kbd/shift? event)
|
||||
(extend-select selected object-id groups)))))))
|
||||
(when local?
|
||||
(let [pos (dom/get-client-position event)
|
||||
top (:y pos)
|
||||
left (- (:x pos) 20)]
|
||||
(dom/prevent-default event)
|
||||
(when-not (contains? selected-objects object-id)
|
||||
(on-clear-selection))
|
||||
(swap! state assoc :menu-open true
|
||||
:top top
|
||||
:left left
|
||||
:object-id object-id))))))
|
||||
|
||||
create-group
|
||||
(mf/use-callback
|
||||
(mf/deps objects selected)
|
||||
(mf/deps objects selected-objects on-clear-selection)
|
||||
(fn [name]
|
||||
(swap! state assoc :selected empty-selection)
|
||||
(st/emit! (dwc/start-undo-transaction))
|
||||
(on-clear-selection)
|
||||
(st/emit! (dwu/start-undo-transaction))
|
||||
(apply st/emit!
|
||||
(->> objects
|
||||
(filter #(contains? selected (:id %)))
|
||||
(filter #(contains? selected-objects (:id %)))
|
||||
(map #(dwl/rename-media
|
||||
(:id %)
|
||||
(str name " / "
|
||||
(cp/merge-path-item (:path %) (:name %)))))))
|
||||
(st/emit! (dwc/commit-undo-transaction))))
|
||||
(st/emit! (dwu/commit-undo-transaction))))
|
||||
|
||||
on-fold-group
|
||||
(mf/use-callback
|
||||
|
@ -466,7 +405,7 @@
|
|||
|
||||
on-group
|
||||
(mf/use-callback
|
||||
(mf/deps objects selected)
|
||||
(mf/deps objects selected-objects)
|
||||
(fn [event]
|
||||
(dom/stop-propagation event)
|
||||
(modal/show! :create-group-dialog {:create create-group})))
|
||||
|
@ -479,11 +418,7 @@
|
|||
(dnd/set-data! event "text/asset-type" mtype)
|
||||
(dnd/set-allowed-effect! event "move")))]
|
||||
|
||||
(mf/use-effect
|
||||
(mf/deps [change-selected selected])
|
||||
#(change-selected (count selected)))
|
||||
|
||||
[:div.asset-section {:on-click unselect-all}
|
||||
[:div.asset-section
|
||||
[:div.asset-title {:class (when (not open?) "closed")}
|
||||
[:span {:on-click (st/emitf (dwl/set-assets-box-open file-id :graphics (not open?)))}
|
||||
i/arrow-slide (tr "workspace.assets.graphics")]
|
||||
|
@ -494,7 +429,7 @@
|
|||
[:& file-uploader {:accept cm/str-media-types
|
||||
:multi true
|
||||
:input-ref input-ref
|
||||
:on-selected on-selected}]])]
|
||||
:on-selected on-file-selected}]])]
|
||||
(when open?
|
||||
(for [group groups]
|
||||
(let [path (first group)
|
||||
|
@ -518,11 +453,11 @@
|
|||
(for [object objects]
|
||||
[:div {:key (:id object)
|
||||
:class-name (dom/classnames
|
||||
:selected (contains? selected (:id object))
|
||||
:selected (contains? selected-objects (:id object))
|
||||
:grid-cell @listing-thumbs?
|
||||
:enum-item (not @listing-thumbs?))
|
||||
:draggable true
|
||||
:on-click (on-select (:id object))
|
||||
:on-click #(on-asset-click % (:id object) groups nil)
|
||||
:on-context-menu (on-context-menu (:id object))
|
||||
:on-drag-start (partial on-drag-start object)}
|
||||
[:img {:src (cfg/resolve-file-media object true)
|
||||
|
@ -552,16 +487,18 @@
|
|||
:on-close #(swap! state assoc :menu-open false)
|
||||
:top (:top @state)
|
||||
:left (:left @state)
|
||||
:options [(when (<= (count selected) 1)
|
||||
:options [(when-not (or multi-objects? multi-assets?)
|
||||
[(tr "workspace.assets.rename") on-rename])
|
||||
[(tr "workspace.assets.delete") on-delete]
|
||||
[(tr "workspace.assets.group") on-group]]}])]))
|
||||
(when-not multi-assets?
|
||||
[(tr "workspace.assets.group") on-group])]}])]))
|
||||
|
||||
|
||||
;; ---- Colors box ----
|
||||
|
||||
(mf/defc color-item
|
||||
[{:keys [color local? file-id selected on-select locale] :as props}]
|
||||
[{:keys [color local? file-id selected-colors multi-colors? multi-assets?
|
||||
on-asset-click on-assets-delete on-clear-selection colors locale] :as props}]
|
||||
(let [rename? (= (:color-for-rename @refs/workspace-local) (:id color))
|
||||
id (:id color)
|
||||
input-ref (mf/use-ref)
|
||||
|
@ -575,10 +512,8 @@
|
|||
(:color color) (:color color)
|
||||
:else (:value color))
|
||||
|
||||
click-color
|
||||
(fn [event]
|
||||
(when on-select
|
||||
((on-select (:id color)) event))
|
||||
apply-color
|
||||
(fn [color-id event]
|
||||
(let [ids (get-in @st/state [:workspace-local :selected])]
|
||||
(if (kbd/shift? event)
|
||||
(st/emit! (dc/change-stroke ids color))
|
||||
|
@ -594,13 +529,12 @@
|
|||
(st/emit! (dwl/update-color updated-color file-id))))
|
||||
|
||||
delete-color
|
||||
(fn []
|
||||
(if (empty? selected)
|
||||
(st/emit! (dwl/delete-color color))
|
||||
(do
|
||||
(st/emit! (dwc/start-undo-transaction))
|
||||
(apply st/emit! (map #(dwl/delete-color {:id %}) selected))
|
||||
(st/emit! (dwc/commit-undo-transaction)))))
|
||||
(mf/use-callback
|
||||
(mf/deps @state multi-colors? multi-assets?)
|
||||
(fn []
|
||||
(if (or multi-colors? multi-assets?)
|
||||
(on-assets-delete)
|
||||
(st/emit! (dwl/delete-color color)))))
|
||||
|
||||
rename-color-clicked
|
||||
(fn [event]
|
||||
|
@ -634,16 +568,20 @@
|
|||
:position :right}))
|
||||
|
||||
on-context-menu
|
||||
(fn [event]
|
||||
(when local?
|
||||
(let [pos (dom/get-client-position event)
|
||||
top (:y pos)
|
||||
left (+ 10 (:x pos))]
|
||||
(dom/prevent-default event)
|
||||
(swap! state assoc
|
||||
:menu-open true
|
||||
:top top
|
||||
:left left))))]
|
||||
(mf/use-callback
|
||||
(mf/deps color selected-colors on-clear-selection)
|
||||
(fn [event]
|
||||
(when local?
|
||||
(let [pos (dom/get-client-position event)
|
||||
top (:y pos)
|
||||
left (+ 10 (:x pos))]
|
||||
(dom/prevent-default event)
|
||||
(when-not (contains? selected-colors (:id color))
|
||||
(on-clear-selection))
|
||||
(swap! state assoc
|
||||
:menu-open true
|
||||
:top top
|
||||
:left left)))))]
|
||||
|
||||
(mf/use-effect
|
||||
(mf/deps (:editing @state))
|
||||
|
@ -653,10 +591,11 @@
|
|||
nil))
|
||||
|
||||
[:div.asset-list-item {:class-name (dom/classnames
|
||||
:selected (contains? selected (:id color)))
|
||||
:selected (contains? selected-colors (:id color)))
|
||||
:on-context-menu on-context-menu
|
||||
:on-click (when-not (:editing @state)
|
||||
click-color)}
|
||||
#(on-asset-click % (:id color) {"" colors}
|
||||
(partial apply-color (:id color))))}
|
||||
[:& bc/color-bullet {:color color}]
|
||||
|
||||
(if (:editing @state)
|
||||
|
@ -679,17 +618,20 @@
|
|||
:on-close #(swap! state assoc :menu-open false)
|
||||
:top (:top @state)
|
||||
:left (:left @state)
|
||||
:options [(when (<= (count selected) 1)
|
||||
:options [(when-not (or multi-colors? multi-assets?)
|
||||
[(t locale "workspace.assets.rename") rename-color-clicked])
|
||||
(when (<= (count selected) 1)
|
||||
(when-not (or multi-colors? multi-assets?)
|
||||
[(t locale "workspace.assets.edit") edit-color-clicked])
|
||||
[(t locale "workspace.assets.delete") delete-color]]}])]))
|
||||
|
||||
(mf/defc colors-box
|
||||
[{:keys [file-id local? colors locale open? change-selected] :as props}]
|
||||
(let [state (mf/use-state {:selected empty-selection})
|
||||
|
||||
selected (:selected @state)
|
||||
[{:keys [file-id local? colors locale open? selected-assets
|
||||
on-asset-click on-assets-delete on-clear-selection] :as props}]
|
||||
(let [selected-colors (:colors selected-assets)
|
||||
multi-colors? (> (count selected-colors) 1)
|
||||
multi-assets? (or (not (empty? (:components selected-assets)))
|
||||
(not (empty? (:graphics selected-assets)))
|
||||
(not (empty? (:typographies selected-assets))))
|
||||
|
||||
add-color
|
||||
(mf/use-callback
|
||||
|
@ -708,32 +650,9 @@
|
|||
:on-accept add-color
|
||||
:data {:color "#406280"
|
||||
:opacity 1}
|
||||
:position :right})))
|
||||
:position :right})))]
|
||||
|
||||
unselect-all
|
||||
(mf/use-callback
|
||||
(fn [event]
|
||||
(swap! state assoc :selected empty-selection)))
|
||||
|
||||
on-select
|
||||
(mf/use-callback
|
||||
(fn [color-id]
|
||||
(fn [event]
|
||||
(dom/stop-propagation event)
|
||||
(swap! state update :selected
|
||||
(fn [selected]
|
||||
(cond
|
||||
(kbd/ctrl? event)
|
||||
(toggle-select selected color-id)
|
||||
|
||||
(kbd/shift? event)
|
||||
(extend-select selected color-id {"" colors})))))))]
|
||||
|
||||
(mf/use-effect
|
||||
(mf/deps [change-selected selected])
|
||||
#(change-selected (count selected)))
|
||||
|
||||
[:div.asset-section {:on-click unselect-all}
|
||||
[:div.asset-section
|
||||
[:div.asset-title {:class (when (not open?) "closed")}
|
||||
[:span {:on-click (st/emitf (dwl/set-assets-box-open file-id :colors (not open?)))}
|
||||
i/arrow-slide (t locale "workspace.assets.colors")]
|
||||
|
@ -751,24 +670,34 @@
|
|||
:color color
|
||||
:file-id file-id
|
||||
:local? local?
|
||||
:selected selected
|
||||
:on-select on-select
|
||||
:selected-colors selected-colors
|
||||
:multi-colors? multi-colors?
|
||||
:multi-assets? multi-assets?
|
||||
:on-asset-click on-asset-click
|
||||
:on-assets-delete on-assets-delete
|
||||
:on-clear-selection on-clear-selection
|
||||
:colors colors
|
||||
:locale locale}]))])]))
|
||||
|
||||
|
||||
;; ---- Typography box ----
|
||||
|
||||
(mf/defc typography-box
|
||||
[{:keys [file file-id local? typographies locale open? change-selected] :as props}]
|
||||
|
||||
[{:keys [file file-id local? typographies locale open? selected-assets
|
||||
on-asset-click on-assets-delete on-clear-selection] :as props}]
|
||||
(let [state (mf/use-state {:detail-open? false
|
||||
:menu-open? false
|
||||
:top nil
|
||||
:left nil
|
||||
:selected empty-selection})
|
||||
:id nil})
|
||||
|
||||
local (deref refs/workspace-local)
|
||||
selected (:selected @state)
|
||||
|
||||
selected-typographies (:typographies selected-assets)
|
||||
multi-typographies? (> (count selected-typographies) 1)
|
||||
multi-assets? (or (not (empty? (:graphics selected-assets)))
|
||||
(not (empty? (:colors selected-assets)))
|
||||
(not (empty? (:typographies selected-assets))))
|
||||
|
||||
add-typography
|
||||
(mf/use-callback
|
||||
|
@ -782,19 +711,8 @@
|
|||
(fn [typography changes]
|
||||
(st/emit! (dwl/update-typography (merge typography changes) file-id))))
|
||||
|
||||
handle-typography-selection
|
||||
apply-typography
|
||||
(fn [typography event]
|
||||
(dom/stop-propagation event)
|
||||
|
||||
(swap! state update :selected
|
||||
(fn [selected]
|
||||
(cond
|
||||
(kbd/ctrl? event)
|
||||
(toggle-select selected (:id typography))
|
||||
|
||||
(kbd/shift? event)
|
||||
(extend-select selected (:id typography) {"" typographies}))))
|
||||
|
||||
(let [ids (get-in @st/state [:workspace-local :selected])
|
||||
attrs (merge
|
||||
{:typography-ref-file file-id
|
||||
|
@ -804,23 +722,21 @@
|
|||
ids)))
|
||||
|
||||
on-context-menu
|
||||
(fn [id event]
|
||||
|
||||
(when local?
|
||||
(let [pos (dom/get-client-position event)
|
||||
top (:y pos)
|
||||
left (- (:x pos) 20)]
|
||||
(dom/prevent-default event)
|
||||
(swap! state assoc
|
||||
:menu-open? true
|
||||
:top top
|
||||
:left left
|
||||
:id id))))
|
||||
|
||||
unselect-all
|
||||
(mf/use-callback
|
||||
(fn [event]
|
||||
(swap! state assoc :selected empty-selection)))
|
||||
(mf/deps selected-typographies on-clear-selection)
|
||||
(fn [id event]
|
||||
(when local?
|
||||
(let [pos (dom/get-client-position event)
|
||||
top (:y pos)
|
||||
left (- (:x pos) 20)]
|
||||
(dom/prevent-default event)
|
||||
(when-not (contains? selected-typographies id)
|
||||
(on-clear-selection))
|
||||
(swap! state assoc
|
||||
:menu-open? true
|
||||
:top top
|
||||
:left left
|
||||
:id id)))))
|
||||
|
||||
closed-typography-edit
|
||||
(mf/use-callback
|
||||
|
@ -836,13 +752,12 @@
|
|||
(st/emit! #(assoc-in % [:workspace-local :edit-typography] (:id @state))))
|
||||
|
||||
handle-delete-typography
|
||||
(fn []
|
||||
(if (empty? selected)
|
||||
(st/emit! (dwl/delete-typography (:id @state)))
|
||||
(do
|
||||
(st/emit! (dwc/start-undo-transaction))
|
||||
(apply st/emit! (map #(dwl/delete-typography %) selected))
|
||||
(st/emit! (dwc/commit-undo-transaction)))))
|
||||
(mf/use-callback
|
||||
(mf/deps @state multi-typographies? multi-assets?)
|
||||
(fn []
|
||||
(if (or multi-typographies? multi-assets?)
|
||||
(on-assets-delete)
|
||||
(st/emit! (dwl/delete-typography (:id @state))))))
|
||||
|
||||
editting-id (or (:rename-typography local) (:edit-typography local))]
|
||||
|
||||
|
@ -854,11 +769,7 @@
|
|||
(when (:edit-typography local)
|
||||
(st/emit! #(update % :workspace-local dissoc :edit-typography)))))
|
||||
|
||||
(mf/use-effect
|
||||
(mf/deps [change-selected selected])
|
||||
#(change-selected (count selected)))
|
||||
|
||||
[:div.asset-section {:on-click unselect-all}
|
||||
[:div.asset-section
|
||||
[:div.asset-title {:class (when (not open?) "closed")}
|
||||
[:span {:on-click (st/emitf (dwl/set-assets-box-open file-id :typographies (not open?)))}
|
||||
i/arrow-slide (t locale "workspace.assets.typography")]
|
||||
|
@ -872,9 +783,9 @@
|
|||
:on-close #(swap! state assoc :menu-open? false)
|
||||
:top (:top @state)
|
||||
:left (:left @state)
|
||||
:options [(when (<= (count selected) 1)
|
||||
:options [(when-not (or multi-typographies? multi-assets?)
|
||||
[(t locale "workspace.assets.rename") handle-rename-typography-clicked])
|
||||
(when (<= (count selected) 1)
|
||||
(when-not (or multi-typographies? multi-assets?)
|
||||
[(t locale "workspace.assets.edit") handle-edit-typography-clicked])
|
||||
[(t locale "workspace.assets.delete") handle-delete-typography]]}]
|
||||
(when open?
|
||||
|
@ -887,8 +798,9 @@
|
|||
:read-only? (not local?)
|
||||
:on-context-menu #(on-context-menu (:id typography) %)
|
||||
:on-change #(handle-change typography %)
|
||||
:selected? (contains? selected (:id typography))
|
||||
:on-select #(handle-typography-selection typography %)
|
||||
:selected? (contains? selected-typographies (:id typography))
|
||||
:on-click #(on-asset-click % (:id typography) {"" typographies}
|
||||
(partial apply-typography typography))
|
||||
:editting? (= editting-id (:id typography))
|
||||
:focus-name? (= (:rename-typography local) (:id typography))}])])]))
|
||||
|
||||
|
@ -961,16 +873,19 @@
|
|||
|
||||
reverse-sort? (mf/use-state false)
|
||||
listing-thumbs? (mf/use-state true)
|
||||
selected-count (mf/use-state {:components 0
|
||||
:graphics 0
|
||||
:colors 0
|
||||
:typographies 0})
|
||||
|
||||
selected-assets (mf/use-state {:components #{}
|
||||
:graphics #{}
|
||||
:colors #{}
|
||||
:typographies #{}})
|
||||
|
||||
selected-count (+ (count (:components @selected-assets))
|
||||
(count (:graphics @selected-assets))
|
||||
(count (:colors @selected-assets))
|
||||
(count (:typographies @selected-assets)))
|
||||
|
||||
toggle-open (st/emitf (dwl/set-assets-box-open (:id file) :library (not open?)))
|
||||
|
||||
change-selected-count (fn [asset-type cnt]
|
||||
(swap! selected-count assoc asset-type cnt))
|
||||
|
||||
url (rt/resolve router :workspace
|
||||
{:project-id (:project-id file)
|
||||
:file-id (:id file)}
|
||||
|
@ -996,9 +911,84 @@
|
|||
toggle-listing
|
||||
(mf/use-callback
|
||||
(fn [event]
|
||||
(swap! listing-thumbs? not)))]
|
||||
(swap! listing-thumbs? not)))
|
||||
|
||||
[:div.tool-window {:on-context-menu #(dom/prevent-default %)}
|
||||
toggle-selected-asset
|
||||
(mf/use-callback
|
||||
(mf/deps @selected-assets)
|
||||
(fn [asset-type asset-id]
|
||||
(swap! selected-assets update asset-type
|
||||
(fn [selected]
|
||||
(if (contains? selected asset-id)
|
||||
(disj selected asset-id)
|
||||
(conj selected asset-id))))))
|
||||
|
||||
extend-selected-assets
|
||||
(mf/use-callback
|
||||
(mf/deps @selected-assets)
|
||||
(fn [asset-type asset-id asset-groups]
|
||||
(swap! selected-assets update asset-type
|
||||
(fn [selected]
|
||||
(let [all-assets (-> asset-groups vals flatten)
|
||||
clicked-idx (d/index-of-pred all-assets #(= (:id %) asset-id))
|
||||
selected-idx (->> selected
|
||||
(map (fn [id]
|
||||
(d/index-of-pred all-assets
|
||||
#(= (:id %) id)))))
|
||||
min-idx (apply min (conj selected-idx clicked-idx))
|
||||
max-idx (apply max (conj selected-idx clicked-idx))]
|
||||
|
||||
(->> all-assets
|
||||
d/enumerate
|
||||
(filter #(<= min-idx (first %) max-idx))
|
||||
(map #(-> % second :id))
|
||||
set))))))
|
||||
|
||||
unselect-all
|
||||
(mf/use-callback
|
||||
(fn []
|
||||
(swap! selected-assets {:components #{}
|
||||
:graphics #{}
|
||||
:colors #{}
|
||||
:typographies #{}})))
|
||||
|
||||
on-asset-click
|
||||
(mf/use-callback
|
||||
(mf/deps toggle-selected-asset extend-selected-assets)
|
||||
(fn [asset-type event asset-id all-assets default-click]
|
||||
(cond
|
||||
(kbd/ctrl? event)
|
||||
(do
|
||||
(dom/stop-propagation event)
|
||||
(toggle-selected-asset asset-type asset-id))
|
||||
|
||||
(kbd/shift? event)
|
||||
(do
|
||||
(dom/stop-propagation event)
|
||||
(extend-selected-assets asset-type asset-id all-assets))
|
||||
|
||||
:else
|
||||
(when default-click
|
||||
(default-click event)))))
|
||||
|
||||
on-assets-delete
|
||||
(mf/use-callback
|
||||
(mf/deps @selected-assets)
|
||||
(fn []
|
||||
(do
|
||||
(st/emit! (dwu/start-undo-transaction))
|
||||
(apply st/emit! (map #(dwl/delete-component {:id %})
|
||||
(:components @selected-assets)))
|
||||
(apply st/emit! (map #(dwl/delete-media {:id %})
|
||||
(:graphics @selected-assets)))
|
||||
(apply st/emit! (map #(dwl/delete-color {:id %})
|
||||
(:colors @selected-assets)))
|
||||
(apply st/emit! (map #(dwl/delete-typography %)
|
||||
(:typographies @selected-assets)))
|
||||
(st/emit! (dwu/commit-undo-transaction)))))]
|
||||
|
||||
[:div.tool-window {:on-context-menu #(dom/prevent-default %)
|
||||
:on-click unselect-all}
|
||||
[:div.tool-window-bar.library-bar
|
||||
{:on-click toggle-open}
|
||||
[:div.collapse-library
|
||||
|
@ -1037,14 +1027,9 @@
|
|||
(str/empty? (:term filters))))]
|
||||
[:div.tool-window-content
|
||||
[:div.listing-options
|
||||
[:span.selected-count
|
||||
(let [selected-count @selected-count
|
||||
total (+ (:components selected-count)
|
||||
(:graphics selected-count)
|
||||
(:colors selected-count)
|
||||
(:typographies selected-count))]
|
||||
(when (> total 0)
|
||||
(tr "workspace.assets.selected-count" (i18n/c total))))]
|
||||
(when (> selected-count 0)
|
||||
[:span.selected-count
|
||||
(tr "workspace.assets.selected-count" (i18n/c selected-count))])
|
||||
[:div.listing-option-btn.first {:on-click toggle-sort}
|
||||
(if @reverse-sort?
|
||||
i/sort-descending
|
||||
|
@ -1053,30 +1038,38 @@
|
|||
(if @listing-thumbs?
|
||||
i/listing-enum
|
||||
i/listing-thumbs)]]
|
||||
|
||||
(when show-components?
|
||||
[:& components-box {:file-id (:id file)
|
||||
:local? local?
|
||||
:components components
|
||||
:listing-thumbs? listing-thumbs?
|
||||
:change-selected (partial change-selected-count
|
||||
:components)
|
||||
:open? (open-box? :components)}])
|
||||
:open? (open-box? :components)
|
||||
:selected-assets @selected-assets
|
||||
:on-asset-click (partial on-asset-click :components)
|
||||
:on-assets-delete on-assets-delete
|
||||
:on-clear-selection unselect-all}])
|
||||
|
||||
(when show-graphics?
|
||||
[:& graphics-box {:file-id (:id file)
|
||||
:local? local?
|
||||
:objects media
|
||||
:listing-thumbs? listing-thumbs?
|
||||
:change-selected (partial change-selected-count
|
||||
:graphics)
|
||||
:open? (open-box? :graphics)}])
|
||||
:open? (open-box? :graphics)
|
||||
:selected-assets @selected-assets
|
||||
:on-asset-click (partial on-asset-click :graphics)
|
||||
:on-assets-delete on-assets-delete
|
||||
:on-clear-selection unselect-all}])
|
||||
(when show-colors?
|
||||
[:& colors-box {:file-id (:id file)
|
||||
:local? local?
|
||||
:locale locale
|
||||
:colors colors
|
||||
:open? (open-box? :colors)
|
||||
:change-selected (partial change-selected-count
|
||||
:colors)}])
|
||||
:selected-assets @selected-assets
|
||||
:on-asset-click (partial on-asset-click :colors)
|
||||
:on-assets-delete on-assets-delete
|
||||
:on-clear-selection unselect-all}])
|
||||
|
||||
(when show-typography?
|
||||
[:& typography-box {:file file
|
||||
|
@ -1085,8 +1078,10 @@
|
|||
:locale locale
|
||||
:typographies typographies
|
||||
:open? (open-box? :typographies)
|
||||
:change-selected (partial change-selected-count
|
||||
:typographies)}])
|
||||
:selected-assets @selected-assets
|
||||
:on-asset-click (partial on-asset-click :typographies)
|
||||
:on-assets-delete on-assets-delete
|
||||
:on-clear-selection unselect-all}])
|
||||
|
||||
(when (and (not show-components?) (not show-graphics?) (not show-colors?))
|
||||
[:div.asset-section
|
||||
|
|
|
@ -285,27 +285,30 @@
|
|||
:objects objects
|
||||
:key id}])))]]))
|
||||
|
||||
(defn- strip-obj-data [obj]
|
||||
(select-keys obj [:id
|
||||
:name
|
||||
:blocked
|
||||
:hidden
|
||||
:shapes
|
||||
:type
|
||||
:content
|
||||
:parent-id
|
||||
:component-id
|
||||
:component-file
|
||||
:shape-ref
|
||||
:touched
|
||||
:metadata
|
||||
:masked-group?]))
|
||||
|
||||
(defn- strip-objects
|
||||
[objects]
|
||||
(let [strip-data #(select-keys % [:id
|
||||
:name
|
||||
:blocked
|
||||
:hidden
|
||||
:shapes
|
||||
:type
|
||||
:content
|
||||
:parent-id
|
||||
:component-id
|
||||
:component-file
|
||||
:shape-ref
|
||||
:touched
|
||||
:metadata
|
||||
:masked-group?])]
|
||||
(persistent!
|
||||
(reduce-kv (fn [res id obj]
|
||||
(assoc! res id (strip-data obj)))
|
||||
(transient {})
|
||||
objects))))
|
||||
(persistent!
|
||||
(->> objects
|
||||
(reduce-kv
|
||||
(fn [res id obj]
|
||||
(assoc! res id (strip-obj-data obj)))
|
||||
(transient {})))))
|
||||
|
||||
(mf/defc layers-tree-wrapper
|
||||
{::mf/wrap-props false
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
[app.common.data :as d]
|
||||
[app.common.uuid :as uuid]
|
||||
[app.main.data.workspace.common :as dwc]
|
||||
[app.main.data.workspace.changes :as dch]
|
||||
[app.main.store :as st]
|
||||
[app.main.ui.icons :as i]
|
||||
[app.main.ui.workspace.sidebar.options.common :refer [advanced-options]]
|
||||
|
@ -32,7 +33,7 @@
|
|||
has-value? (not (nil? blur))
|
||||
multiple? (= blur :multiple)
|
||||
|
||||
change! (fn [update-fn] (st/emit! (dwc/update-shapes ids update-fn)))
|
||||
change! (fn [update-fn] (st/emit! (dch/update-shapes ids update-fn)))
|
||||
|
||||
handle-add
|
||||
(fn []
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
[app.main.ui.components.context-menu :refer [context-menu]]
|
||||
[app.main.data.workspace :as dw]
|
||||
[app.main.data.workspace.common :as dwc]
|
||||
[app.main.data.workspace.undo :as dwu]
|
||||
[app.main.data.workspace.libraries :as dwl]
|
||||
[app.util.i18n :as i18n :refer [t]]
|
||||
[app.util.dom :as dom]))
|
||||
|
@ -50,10 +51,10 @@
|
|||
do-detach-component (st/emitf (dwl/detach-component id))
|
||||
do-reset-component (st/emitf (dwl/reset-component id))
|
||||
do-update-component (st/emitf
|
||||
(dwc/start-undo-transaction)
|
||||
(dwu/start-undo-transaction)
|
||||
(dwl/update-component id)
|
||||
(dwl/sync-file current-file-id current-file-id)
|
||||
(dwc/commit-undo-transaction))
|
||||
(dwu/commit-undo-transaction))
|
||||
confirm-update-remote-component (st/emitf
|
||||
(dwl/update-component id)
|
||||
(dwl/sync-file current-file-id
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
[app.common.pages :as cp]
|
||||
[app.main.data.workspace.colors :as dc]
|
||||
[app.main.data.workspace.common :as dwc]
|
||||
[app.main.data.workspace.undo :as dwu]
|
||||
[app.main.data.workspace.texts :as dwt]
|
||||
[app.main.refs :as refs]
|
||||
[app.main.store :as st]
|
||||
|
@ -78,13 +79,13 @@
|
|||
(mf/use-callback
|
||||
(mf/deps ids)
|
||||
(fn [value opacity id file-id]
|
||||
(st/emit! (dwc/start-undo-transaction))))
|
||||
(st/emit! (dwu/start-undo-transaction))))
|
||||
|
||||
on-close-picker
|
||||
(mf/use-callback
|
||||
(mf/deps ids)
|
||||
(fn [value opacity id file-id]
|
||||
(st/emit! (dwc/commit-undo-transaction))))]
|
||||
(st/emit! (dwu/commit-undo-transaction))))]
|
||||
|
||||
(if show?
|
||||
[:div.element-set
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
[app.common.data :as d]
|
||||
[app.common.math :as mth]
|
||||
[app.main.data.workspace.common :as dwc]
|
||||
[app.main.data.workspace.changes :as dch]
|
||||
[app.main.store :as st]
|
||||
[app.main.ui.components.numeric-input :refer [numeric-input]]
|
||||
[app.main.ui.icons :as i]
|
||||
|
@ -34,7 +35,7 @@
|
|||
(mf/use-callback
|
||||
(mf/deps ids)
|
||||
(fn [prop value]
|
||||
(st/emit! (dwc/update-shapes ids #(assoc % prop value)))))
|
||||
(st/emit! (dch/update-shapes ids #(assoc % prop value)))))
|
||||
|
||||
handle-change-blend-mode
|
||||
(mf/use-callback
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
[app.common.geom.point :as gpt]
|
||||
[app.main.data.workspace :as udw]
|
||||
[app.main.data.workspace.common :as dwc]
|
||||
[app.main.data.workspace.changes :as dch]
|
||||
[app.main.ui.components.numeric-input :refer [numeric-input]]
|
||||
[app.common.math :as math]
|
||||
[app.util.i18n :refer [t] :as i18n]))
|
||||
|
@ -106,7 +107,7 @@
|
|||
(:r1 shape)
|
||||
(-> (assoc :rx 0 :ry 0)
|
||||
(dissoc :r1 :r2 :r3 :r4))))]
|
||||
(st/emit! (dwc/update-shapes ids-with-children radius-update)))))
|
||||
(st/emit! (dch/update-shapes ids-with-children radius-update)))))
|
||||
|
||||
on-switch-to-radius-4
|
||||
(mf/use-callback
|
||||
|
@ -118,7 +119,7 @@
|
|||
(:rx shape)
|
||||
(-> (assoc :r1 0 :r2 0 :r3 0 :r4 0)
|
||||
(dissoc :rx :ry))))]
|
||||
(st/emit! (dwc/update-shapes ids-with-children radius-update)))))
|
||||
(st/emit! (dch/update-shapes ids-with-children radius-update)))))
|
||||
|
||||
on-radius-1-change
|
||||
(mf/use-callback
|
||||
|
@ -134,7 +135,7 @@
|
|||
(or (:rx shape) (:r1 shape))
|
||||
(assoc :rx value :ry value)))]
|
||||
|
||||
(st/emit! (dwc/update-shapes ids-with-children radius-update)))))
|
||||
(st/emit! (dch/update-shapes ids-with-children radius-update)))))
|
||||
|
||||
on-radius-4-change
|
||||
(mf/use-callback
|
||||
|
@ -150,7 +151,7 @@
|
|||
(attr shape)
|
||||
(assoc attr value)))]
|
||||
|
||||
(st/emit! (dwc/update-shapes ids-with-children radius-update)))))
|
||||
(st/emit! (dch/update-shapes ids-with-children radius-update)))))
|
||||
|
||||
on-width-change #(on-size-change % :width)
|
||||
on-height-change #(on-size-change % :height)
|
||||
|
|
|
@ -10,6 +10,8 @@
|
|||
[app.common.data :as d]
|
||||
[app.common.uuid :as uuid]
|
||||
[app.main.data.workspace.common :as dwc]
|
||||
[app.main.data.workspace.changes :as dch]
|
||||
[app.main.data.workspace.undo :as dwu]
|
||||
[app.main.store :as st]
|
||||
[app.main.ui.icons :as i]
|
||||
[app.main.ui.components.numeric-input :refer [numeric-input]]
|
||||
|
@ -56,7 +58,7 @@
|
|||
on-remove-shadow
|
||||
(fn [index]
|
||||
(fn []
|
||||
(st/emit! (dwc/update-shapes ids #(update % :shadow remove-shadow-by-index index) ))))
|
||||
(st/emit! (dch/update-shapes ids #(update % :shadow remove-shadow-by-index index) ))))
|
||||
|
||||
select-text
|
||||
(fn [ref] (fn [event] (dom/select-text! (mf/ref-val ref))))
|
||||
|
@ -69,14 +71,14 @@
|
|||
([index attr valid? update-ref]
|
||||
(fn [value]
|
||||
(when (or (not valid?) (valid? value))
|
||||
(do (st/emit! (dwc/update-shapes ids #(assoc-in % [:shadow index attr] value)))
|
||||
(do (st/emit! (dch/update-shapes ids #(assoc-in % [:shadow index attr] value)))
|
||||
(when update-ref (dom/set-value! (mf/ref-val update-ref) value)))))))
|
||||
|
||||
update-color
|
||||
(fn [index]
|
||||
(fn [color opacity]
|
||||
(let [color (d/without-keys color [:id :file-id :gradient])]
|
||||
(st/emit! (dwc/update-shapes
|
||||
(st/emit! (dch/update-shapes
|
||||
ids
|
||||
#(-> %
|
||||
(assoc-in [:shadow index :color] color)
|
||||
|
@ -86,7 +88,7 @@
|
|||
(fn [index]
|
||||
(fn [color opacity]
|
||||
(if-not (string? (:color value))
|
||||
(st/emit! (dwc/update-shapes
|
||||
(st/emit! (dch/update-shapes
|
||||
ids
|
||||
#(assoc-in % [:shadow index :color]
|
||||
(dissoc (:color value) :id :file-id)))))))
|
||||
|
@ -94,7 +96,7 @@
|
|||
toggle-visibility
|
||||
(fn [index]
|
||||
(fn []
|
||||
(st/emit! (dwc/update-shapes ids #(update-in % [:shadow index :hidden] not)))))]
|
||||
(st/emit! (dch/update-shapes ids #(update-in % [:shadow index :hidden] not)))))]
|
||||
[:*
|
||||
[:div.element-set-options-group
|
||||
|
||||
|
@ -129,7 +131,7 @@
|
|||
{:default-value (str (:style value))
|
||||
:on-change (fn [event]
|
||||
(let [value (-> event dom/get-target dom/get-value d/read-string)]
|
||||
(st/emit! (dwc/update-shapes ids #(assoc-in % [:shadow index :style] value)))))}
|
||||
(st/emit! (dch/update-shapes ids #(assoc-in % [:shadow index :style] value)))))}
|
||||
[:option {:value ":drop-shadow"} (t locale "workspace.options.shadow-options.drop-shadow")]
|
||||
[:option {:value ":inner-shadow"} (t locale "workspace.options.shadow-options.inner-shadow")]]]
|
||||
|
||||
|
@ -181,18 +183,18 @@
|
|||
:disable-gradient true
|
||||
:on-change (update-color index)
|
||||
:on-detach (detach-color index)
|
||||
:on-open #(st/emit! (dwc/start-undo-transaction))
|
||||
:on-close #(st/emit! (dwc/commit-undo-transaction))}]]]]))
|
||||
:on-open #(st/emit! (dwu/start-undo-transaction))
|
||||
:on-close #(st/emit! (dwu/commit-undo-transaction))}]]]]))
|
||||
(mf/defc shadow-menu
|
||||
[{:keys [ids type values] :as props}]
|
||||
(let [locale (i18n/use-locale)
|
||||
on-remove-all-shadows
|
||||
(fn [event]
|
||||
(st/emit! (dwc/update-shapes ids #(dissoc % :shadow) )))
|
||||
(st/emit! (dch/update-shapes ids #(dissoc % :shadow) )))
|
||||
|
||||
on-add-shadow
|
||||
(fn []
|
||||
(st/emit! (dwc/update-shapes ids #(update % :shadow (fnil conj []) (create-shadow)) )))]
|
||||
(st/emit! (dch/update-shapes ids #(update % :shadow (fnil conj []) (create-shadow)) )))]
|
||||
[:div.element-set.shadow-options
|
||||
[:div.element-set-title
|
||||
[:span
|
||||
|
|
|
@ -11,6 +11,8 @@
|
|||
[app.common.data :as d]
|
||||
[app.common.math :as math]
|
||||
[app.main.data.workspace.common :as dwc]
|
||||
[app.main.data.workspace.undo :as dwu]
|
||||
[app.main.data.workspace.changes :as dch]
|
||||
[app.main.data.workspace.colors :as dc]
|
||||
[app.main.store :as st]
|
||||
[app.main.ui.icons :as i]
|
||||
|
@ -78,7 +80,7 @@
|
|||
(let [value (-> (dom/get-target event)
|
||||
(dom/get-value)
|
||||
(d/read-string))]
|
||||
(st/emit! (dwc/update-shapes ids #(assoc % :stroke-style value)))))
|
||||
(st/emit! (dch/update-shapes ids #(assoc % :stroke-style value)))))
|
||||
|
||||
on-stroke-alignment-change
|
||||
(fn [event]
|
||||
|
@ -86,7 +88,7 @@
|
|||
(dom/get-value)
|
||||
(d/read-string))]
|
||||
(when-not (str/empty? value)
|
||||
(st/emit! (dwc/update-shapes ids #(assoc % :stroke-alignment value))))))
|
||||
(st/emit! (dch/update-shapes ids #(assoc % :stroke-alignment value))))))
|
||||
|
||||
on-stroke-width-change
|
||||
(fn [event]
|
||||
|
@ -94,11 +96,11 @@
|
|||
(dom/get-value)
|
||||
(d/parse-integer 0))]
|
||||
(when-not (str/empty? value)
|
||||
(st/emit! (dwc/update-shapes ids #(assoc % :stroke-width value))))))
|
||||
(st/emit! (dch/update-shapes ids #(assoc % :stroke-width value))))))
|
||||
|
||||
on-add-stroke
|
||||
(fn [event]
|
||||
(st/emit! (dwc/update-shapes ids #(assoc %
|
||||
(st/emit! (dch/update-shapes ids #(assoc %
|
||||
:stroke-style :solid
|
||||
:stroke-color "#000000"
|
||||
:stroke-opacity 1
|
||||
|
@ -106,19 +108,19 @@
|
|||
|
||||
on-del-stroke
|
||||
(fn [event]
|
||||
(st/emit! (dwc/update-shapes ids #(assoc % :stroke-style :none))))
|
||||
(st/emit! (dch/update-shapes ids #(assoc % :stroke-style :none))))
|
||||
|
||||
on-open-picker
|
||||
(mf/use-callback
|
||||
(mf/deps ids)
|
||||
(fn [value opacity id file-id]
|
||||
(st/emit! (dwc/start-undo-transaction))))
|
||||
(st/emit! (dwu/start-undo-transaction))))
|
||||
|
||||
on-close-picker
|
||||
(mf/use-callback
|
||||
(mf/deps ids)
|
||||
(fn [value opacity id file-id]
|
||||
(st/emit! (dwc/commit-undo-transaction))))]
|
||||
(st/emit! (dwu/commit-undo-transaction))))]
|
||||
|
||||
(if show-options
|
||||
[:div.element-set
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
[cuerdas.core :as str]
|
||||
[app.common.data :as d]
|
||||
[app.main.data.workspace.common :as dwc]
|
||||
[app.main.data.workspace.changes :as dch]
|
||||
[app.main.store :as st]
|
||||
[app.main.ui.workspace.sidebar.options.rows.input-row :refer [input-row]]
|
||||
[app.util.dom :as dom]
|
||||
|
@ -61,7 +62,7 @@
|
|||
(fn [attr value]
|
||||
(let [update-fn
|
||||
(fn [shape] (assoc-in shape (concat [:svg-attrs] attr) value))]
|
||||
(st/emit! (dwc/update-shapes ids update-fn)))))
|
||||
(st/emit! (dch/update-shapes ids update-fn)))))
|
||||
|
||||
handle-delete
|
||||
(mf/use-callback
|
||||
|
@ -76,7 +77,7 @@
|
|||
(empty? (get-in shape [:svg-attrs :style]))
|
||||
(update :svg-attrs dissoc :style))]
|
||||
shape))]
|
||||
(st/emit! (dwc/update-shapes ids update-fn)))))
|
||||
(st/emit! (dch/update-shapes ids update-fn)))))
|
||||
|
||||
]
|
||||
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
[app.common.uuid :as uuid]
|
||||
[app.common.text :as txt]
|
||||
[app.main.data.workspace.common :as dwc]
|
||||
[app.main.data.workspace.changes :as dch]
|
||||
[app.main.data.workspace.libraries :as dwl]
|
||||
[app.main.data.workspace.texts :as dwt]
|
||||
[app.main.fonts :as fonts]
|
||||
|
@ -159,7 +160,7 @@
|
|||
grow-type (->> values :grow-type)
|
||||
handle-change-grow
|
||||
(fn [event grow-type]
|
||||
(st/emit! (dwc/update-shapes ids #(assoc % :grow-type grow-type))))]
|
||||
(st/emit! (dch/update-shapes ids #(assoc % :grow-type grow-type))))]
|
||||
|
||||
[:div.align-icons
|
||||
[:span.tooltip.tooltip-bottom
|
||||
|
|
|
@ -207,7 +207,7 @@
|
|||
|
||||
|
||||
(mf/defc typography-entry
|
||||
[{:keys [typography read-only? selected? on-select on-change on-detach on-context-menu editting? focus-name? file]}]
|
||||
[{:keys [typography read-only? selected? on-click on-change on-detach on-context-menu editting? focus-name? file]}]
|
||||
(let [locale (mf/deref i18n/locale)
|
||||
open? (mf/use-state editting?)
|
||||
hover-detach (mf/use-state false)
|
||||
|
@ -241,8 +241,8 @@
|
|||
[:div.element-set-options-group.typography-entry
|
||||
{:class (when selected? "selected")}
|
||||
[:div.typography-selection-wrapper
|
||||
{:class (when on-select "is-selectable")
|
||||
:on-click on-select
|
||||
{:class (when on-click "is-selectable")
|
||||
:on-click on-click
|
||||
:on-context-menu on-context-menu}
|
||||
[:div.typography-sample
|
||||
{:style {:font-family (:font-family typography)
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
[app.main.store :as st]
|
||||
[app.main.data.workspace :as dw]
|
||||
[app.main.data.workspace.common :as dwc]
|
||||
[app.main.data.workspace.undo :as dwu]
|
||||
[app.util.i18n :as i18n :refer [t]]
|
||||
[app.main.ui.workspace.sidebar.options.rows.color-row :refer [color-row]]))
|
||||
|
||||
|
@ -31,12 +32,12 @@
|
|||
on-open
|
||||
(mf/use-callback
|
||||
(mf/deps page-id)
|
||||
#(st/emit! (dwc/start-undo-transaction)))
|
||||
#(st/emit! (dwu/start-undo-transaction)))
|
||||
|
||||
on-close
|
||||
(mf/use-callback
|
||||
(mf/deps page-id)
|
||||
#(st/emit! (dwc/commit-undo-transaction)))]
|
||||
#(st/emit! (dwu/commit-undo-transaction)))]
|
||||
|
||||
[:div.element-set
|
||||
[:div.element-set-title (t locale "workspace.options.canvas-background")]
|
||||
|
|
|
@ -102,6 +102,13 @@
|
|||
:index index
|
||||
:name (:name page)})]
|
||||
|
||||
(mf/use-effect
|
||||
(mf/deps selected?)
|
||||
(fn []
|
||||
(when selected?
|
||||
(let [node (mf/ref-val dref)]
|
||||
(.scrollIntoViewIfNeeded ^js node)))))
|
||||
|
||||
(mf/use-layout-effect
|
||||
(mf/deps (:edition @local))
|
||||
(fn []
|
||||
|
|
|
@ -95,10 +95,12 @@
|
|||
;; Only when we have all the selected shapes in one frame
|
||||
selected-frame (when (= (count selected-frames) 1) (get objects (first selected-frames)))
|
||||
|
||||
|
||||
create-comment? (= :comments drawing-tool)
|
||||
drawing-path? (or (and edition (= :draw (get-in edit-path [edition :edit-mode])))
|
||||
(and (some? drawing-obj) (= :path (:type drawing-obj))))
|
||||
text-editing? (and edition (= :text (get-in objects [edition :type])))
|
||||
path-editing? (and edition (= :path (get-in objects [edition :type])))
|
||||
text-editing? (and edition (= :text (get-in objects [edition :type])))
|
||||
|
||||
on-click (actions/on-click hover selected edition drawing-path? drawing-tool)
|
||||
on-context-menu (actions/on-context-menu hover)
|
||||
|
@ -106,7 +108,7 @@
|
|||
on-drag-enter (actions/on-drag-enter)
|
||||
on-drag-over (actions/on-drag-over)
|
||||
on-drop (actions/on-drop file viewport-ref zoom)
|
||||
on-mouse-down (actions/on-mouse-down @hover drawing-tool text-editing? edition edit-path selected)
|
||||
on-mouse-down (actions/on-mouse-down @hover selected edition drawing-tool text-editing? path-editing? drawing-path? create-comment?)
|
||||
on-mouse-up (actions/on-mouse-up disable-paste)
|
||||
on-pointer-down (actions/on-pointer-down)
|
||||
on-pointer-enter (actions/on-pointer-enter in-viewport?)
|
||||
|
|
|
@ -24,13 +24,12 @@
|
|||
[beicon.core :as rx]
|
||||
[cuerdas.core :as str]
|
||||
[rumext.alpha :as mf])
|
||||
(:import goog.events.WheelEvent
|
||||
goog.events.KeyCodes))
|
||||
(:import goog.events.WheelEvent))
|
||||
|
||||
(defn on-mouse-down
|
||||
[{:keys [id blocked hidden type]} drawing-tool text-editing? edition edit-path selected]
|
||||
[{:keys [id blocked hidden type]} selected edition drawing-tool text-editing? path-editing? drawing-path? create-comment?]
|
||||
(mf/use-callback
|
||||
(mf/deps id blocked hidden type drawing-tool text-editing? edition edit-path selected)
|
||||
(mf/deps id blocked hidden type selected edition drawing-tool text-editing? path-editing? drawing-path? create-comment?)
|
||||
(fn [bevent]
|
||||
(when (or (dom/class? (dom/get-target bevent) "viewport-controls")
|
||||
(dom/class? (dom/get-target bevent) "viewport-selrect"))
|
||||
|
@ -45,9 +44,7 @@
|
|||
middle-click? (= 2 (.-which event))
|
||||
|
||||
frame? (= :frame type)
|
||||
selected? (contains? selected id)
|
||||
|
||||
drawing-path? (= :draw (get-in edit-path [edition :edit-mode]))]
|
||||
selected? (contains? selected id)]
|
||||
|
||||
(when middle-click?
|
||||
(dom/prevent-default bevent)
|
||||
|
@ -62,13 +59,13 @@
|
|||
(when (and (not text-editing?)
|
||||
(not blocked)
|
||||
(not hidden)
|
||||
(not (#{:comments :path} drawing-tool))
|
||||
(not create-comment?)
|
||||
(not drawing-path?))
|
||||
(cond
|
||||
drawing-tool
|
||||
(st/emit! (dd/start-drawing drawing-tool))
|
||||
|
||||
(and edit-path (contains? edit-path edition))
|
||||
path-editing?
|
||||
;; Handle path node area selection
|
||||
(st/emit! (dwdp/handle-selection shift?))
|
||||
|
||||
|
@ -263,8 +260,7 @@
|
|||
(mf/use-callback
|
||||
(fn [event]
|
||||
(let [bevent (.getBrowserEvent ^js event)
|
||||
key (.-keyCode ^js event)
|
||||
key (.normalizeKeyCode KeyCodes key)
|
||||
key (.-key ^js event)
|
||||
ctrl? (kbd/ctrl? event)
|
||||
shift? (kbd/shift? event)
|
||||
alt? (kbd/alt? event)
|
||||
|
@ -284,8 +280,7 @@
|
|||
(defn on-key-up []
|
||||
(mf/use-callback
|
||||
(fn [event]
|
||||
(let [key (.-keyCode event)
|
||||
key (.normalizeKeyCode KeyCodes key)
|
||||
(let [key (.-key event)
|
||||
ctrl? (kbd/ctrl? event)
|
||||
shift? (kbd/shift? event)
|
||||
alt? (kbd/alt? event)
|
||||
|
|
|
@ -100,7 +100,8 @@
|
|||
{:cmd :selection/query
|
||||
:page-id page-id
|
||||
:rect rect
|
||||
:include-frames? true}))))
|
||||
:include-frames? true
|
||||
:reverse? true})))) ;; we want the topmost shape to be selected first
|
||||
|
||||
;; We use ref so we don't recreate the stream on a change
|
||||
transform-ref (mf/use-ref nil)
|
||||
|
|
|
@ -19,7 +19,6 @@
|
|||
[app.main.streams :as ms]
|
||||
[app.main.ui.cursors :as cur]
|
||||
[app.main.ui.hooks :as hooks]
|
||||
[app.main.ui.workspace.viewport.outline :refer [outline]]
|
||||
[app.main.ui.workspace.shapes.path.editor :refer [path-editor]]
|
||||
[app.util.data :as d]
|
||||
[app.util.debug :refer [debug?]]
|
||||
|
@ -255,7 +254,6 @@
|
|||
:zoom zoom
|
||||
:color color
|
||||
:on-move-selected on-move-selected}]
|
||||
[:& outline {:shape shape :color color}]
|
||||
|
||||
;; Handlers
|
||||
(for [{:keys [type position props]} (handlers-for-selection selrect shape zoom)]
|
||||
|
|
|
@ -6,10 +6,10 @@
|
|||
|
||||
(ns app.util.keyboard)
|
||||
|
||||
(defn is-keycode?
|
||||
[keycode]
|
||||
(defn is-key?
|
||||
[key]
|
||||
(fn [e]
|
||||
(= (.-keyCode e) keycode)))
|
||||
(= (.-key e) key)))
|
||||
|
||||
(defn ^boolean alt?
|
||||
[event]
|
||||
|
@ -27,8 +27,11 @@
|
|||
[event]
|
||||
(.-shiftKey event))
|
||||
|
||||
(def esc? (is-keycode? 27))
|
||||
(def enter? (is-keycode? 13))
|
||||
(def space? (is-keycode? 32))
|
||||
(def up-arrow? (is-keycode? 38))
|
||||
(def down-arrow? (is-keycode? 40))
|
||||
(def esc? (is-key? "Escape"))
|
||||
(def enter? (is-key? "Enter"))
|
||||
(def space? (is-key? " "))
|
||||
(def up-arrow? (is-key? "ArrowUp"))
|
||||
(def down-arrow? (is-key? "ArrowDown"))
|
||||
(def altKey? (is-key? "Alt"))
|
||||
(def ctrlKey? (or (is-key? "Control")
|
||||
(is-key? "Meta")))
|
||||
|
|
|
@ -41,8 +41,8 @@
|
|||
nil))
|
||||
|
||||
(defmethod impl/handler :selection/query
|
||||
[{:keys [page-id rect frame-id include-frames? include-groups? disabled-masks] :or {include-groups? true
|
||||
disabled-masks #{}} :as message}]
|
||||
[{:keys [page-id rect frame-id include-frames? include-groups? disabled-masks reverse?]
|
||||
:or {include-groups? true disabled-masks #{} reverse? false} :as message}]
|
||||
(when-let [index (get @state page-id)]
|
||||
(let [result (-> (qdt/search index (clj->js rect))
|
||||
(es6-iterator-seq))
|
||||
|
@ -76,11 +76,13 @@
|
|||
(filter (comp overlaps? :frame))
|
||||
(filter (comp overlaps-masks? :masks))
|
||||
(filter overlaps?))
|
||||
result)]
|
||||
result)
|
||||
|
||||
keyfn (if reverse? (comp - :z) :z)]
|
||||
|
||||
(into (d/ordered-set)
|
||||
(->> matching-shapes
|
||||
(sort-by (comp - :z))
|
||||
(sort-by keyfn)
|
||||
(map :id))))))
|
||||
|
||||
(defn create-mask-index
|
||||
|
|
|
@ -1194,6 +1194,11 @@ msgstr "Send invitation"
|
|||
msgid "modals.invite-member.title"
|
||||
msgstr "Invite to join the team"
|
||||
|
||||
msgid "modals.leave-and-reassign.forbiden"
|
||||
msgstr ""
|
||||
"You can not leave the team if there is no other member to promote to owner. "
|
||||
"You might want to delete the team."
|
||||
|
||||
#: src/app/main/ui/dashboard/sidebar.cljs
|
||||
msgid "modals.leave-and-reassign.hint1"
|
||||
msgstr "You are %s owner."
|
||||
|
|
|
@ -1184,6 +1184,11 @@ msgstr "Enviar invitacion"
|
|||
msgid "modals.invite-member.title"
|
||||
msgstr "Invitar a unirse al equipo"
|
||||
|
||||
msgid "modals.leave-and-reassign.forbiden"
|
||||
msgstr ""
|
||||
"No puede abandonar el equipo si no hay otro miembro al que promocionar a "
|
||||
"dueño. Quizás quiere borrar el equipo."
|
||||
|
||||
#: src/app/main/ui/dashboard/sidebar.cljs
|
||||
msgid "modals.leave-and-reassign.hint1"
|
||||
msgstr "Eres %s dueño."
|
||||
|
@ -2505,4 +2510,4 @@ msgid "workspace.updates.update"
|
|||
msgstr "Actualizar"
|
||||
|
||||
msgid "workspace.viewport.click-to-close-path"
|
||||
msgstr "Pulsar para cerrar la ruta"
|
||||
msgstr "Pulsar para cerrar la ruta"
|
Loading…
Add table
Reference in a new issue