mirror of
https://github.com/penpot/penpot.git
synced 2025-01-06 14:50:20 -05:00
♻️ Initial refactor of page data structure (wip).
Still work in progress but is a necessary step for a future (re)introduction of groups.
This commit is contained in:
parent
cbad98b783
commit
ba373573e0
29 changed files with 1116 additions and 787 deletions
|
@ -12,6 +12,7 @@
|
|||
[mount.core :as mount]
|
||||
[promesa.core :as p]
|
||||
[uxbox.config :as cfg]
|
||||
[uxbox.common.pages :as cp]
|
||||
[uxbox.common.data :as d]
|
||||
[uxbox.core]
|
||||
[uxbox.db :as db]
|
||||
|
@ -149,12 +150,7 @@
|
|||
create-page
|
||||
(fn [conn owner-id project-id file-id index]
|
||||
(p/let [id (mk-uuid "page" project-id file-id index)
|
||||
data {:version 1
|
||||
:shapes []
|
||||
:canvas []
|
||||
:options {}
|
||||
:shapes-by-id {}}
|
||||
|
||||
data cp/default-page-data
|
||||
name (str "page " index)
|
||||
version 0
|
||||
ordering index
|
||||
|
|
|
@ -86,3 +86,80 @@
|
|||
'(promesa.core/let)]}}}})
|
||||
(kondo/print!))))
|
||||
|
||||
(comment
|
||||
{:version 1
|
||||
:options {}
|
||||
:shapes [:id1 :id2]
|
||||
:canvas [:id3]
|
||||
:shapes-by-id {:id1 {:canvas :id3} :id2 {} :id3 {}}})
|
||||
|
||||
|
||||
(comment
|
||||
{:version 2
|
||||
:options {}
|
||||
|
||||
:objects
|
||||
{:root
|
||||
{:type :frame
|
||||
:shapes [:sid0 :frame-0]}
|
||||
|
||||
:frame0
|
||||
{:type :frame
|
||||
:parent :root
|
||||
:shapes [:sid1 :sid2]}
|
||||
|
||||
:sid0
|
||||
{:type :rect
|
||||
:parent :root}
|
||||
|
||||
:sid1
|
||||
{:type :rect
|
||||
:parent :frame0}
|
||||
|
||||
:sid2
|
||||
{:type :group
|
||||
:shapes [:sid3 :sid4]
|
||||
:parent :frame0}
|
||||
|
||||
:sid3
|
||||
{:type :elipse
|
||||
:parent :sid2}
|
||||
|
||||
:sid4
|
||||
{:type :elipse
|
||||
:parent :sid2}}})
|
||||
|
||||
(comment
|
||||
{:version 3
|
||||
:options {}
|
||||
|
||||
:rmap
|
||||
{:id1 :root-frame
|
||||
:id2 :root-frame
|
||||
:id3 :frame-id-1
|
||||
:id4 :frame-id-2
|
||||
:id5 :frame-id-2
|
||||
:id6 :frame-id-2}
|
||||
|
||||
:frames
|
||||
{:root-frame
|
||||
{:type :frame
|
||||
:shapes [:id1 :id2]
|
||||
:objects
|
||||
{:id1 {:type :rect}
|
||||
:id2 {:type :elipse}}}
|
||||
|
||||
:frame-id-1
|
||||
{:type :frame
|
||||
:shapes [:id3]
|
||||
:objects
|
||||
{:id3 {:type :path}}}
|
||||
|
||||
:frame-id-2
|
||||
{:type :frame
|
||||
:shapes [:id4]
|
||||
:objects
|
||||
{:id4 {:type :group
|
||||
:shapes [:id5 :id6]}
|
||||
:id5 {:type :path :parent :id4}
|
||||
:id6 {:type :elipse :parent :id4}}}}})
|
||||
|
|
|
@ -57,27 +57,6 @@
|
|||
not-found))
|
||||
not-found coll)))
|
||||
|
||||
(defn diff-maps
|
||||
[ma mb]
|
||||
(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)]
|
||||
(concat
|
||||
(mapv #(vector :set % (get mb %)) added)
|
||||
(mapv #(vector :set % nil) removed)
|
||||
(loop [k (first both)
|
||||
r (rest both)
|
||||
rs []]
|
||||
(if k
|
||||
(let [vma (get ma k)
|
||||
vmb (get mb k)]
|
||||
(if (= vma vmb)
|
||||
(recur (first r) (rest r) rs)
|
||||
(recur (first r) (rest r) (conj rs [:set k vmb]))))
|
||||
rs)))))
|
||||
|
||||
(defn index-by
|
||||
"Return a indexed map of the collection keyed by the result of
|
||||
executing the getter over each element of the collection."
|
||||
|
|
|
@ -1,9 +1,18 @@
|
|||
;; 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/.
|
||||
;;
|
||||
;; This Source Code Form is "Incompatible With Secondary Licenses", as
|
||||
;; defined by the Mozilla Public License, v. 2.0.
|
||||
;;
|
||||
;; Copyright (c) 2019-2020 Andrey Antukh <niwi@niwi.nz>
|
||||
(ns uxbox.common.pages
|
||||
"A common (clj/cljs) functions and specs for pages."
|
||||
(:require
|
||||
[uxbox.common.spec :as us]
|
||||
[clojure.spec.alpha :as s]
|
||||
[uxbox.common.data :as d]))
|
||||
[uxbox.common.data :as d]
|
||||
[uxbox.common.exceptions :as ex]
|
||||
[uxbox.common.spec :as us]))
|
||||
|
||||
;; --- Specs
|
||||
|
||||
|
@ -46,7 +55,7 @@
|
|||
(s/def ::stroke-style #{:none :solid :dotted :dashed :mixed})
|
||||
(s/def ::stroke-width number?)
|
||||
(s/def ::text-align #{"left" "right" "center" "justify"})
|
||||
(s/def ::type #{:rect :path :circle :image :text :canvas :curve :icon})
|
||||
(s/def ::type #{:rect :path :circle :image :text :canvas :curve :icon :frame})
|
||||
(s/def ::x number?)
|
||||
(s/def ::y number?)
|
||||
(s/def ::cx number?)
|
||||
|
@ -92,162 +101,243 @@
|
|||
(s/def ::shapes (s/coll-of uuid? :kind vector?))
|
||||
(s/def ::canvas (s/coll-of uuid? :kind vector?))
|
||||
|
||||
(s/def ::shapes-by-id
|
||||
(s/def ::objects
|
||||
(s/map-of uuid? ::shape))
|
||||
|
||||
(s/def ::data
|
||||
(s/keys :req-un [::shapes
|
||||
::canvas
|
||||
::options
|
||||
::shapes-by-id]))
|
||||
(s/keys :req-un [::options
|
||||
::version
|
||||
::objects]))
|
||||
|
||||
;; Changes related
|
||||
(s/def ::operation (s/tuple #{:set} keyword? any?))
|
||||
(s/def ::attr keyword?)
|
||||
(s/def ::val any?)
|
||||
(s/def ::parent-id uuid?)
|
||||
(s/def ::frame-id uuid?)
|
||||
|
||||
(s/def ::operations
|
||||
(s/coll-of ::operation :kind vector?))
|
||||
(defmulti operation-spec-impl :type)
|
||||
|
||||
(defmethod operation-spec-impl :set [_]
|
||||
(s/keys :req-un [::attr ::val]))
|
||||
|
||||
(defmethod operation-spec-impl :mov [_]
|
||||
(s/keys :req-un [::id ::index]))
|
||||
|
||||
(s/def ::operation (s/multi-spec operation-spec-impl :type))
|
||||
(s/def ::operations (s/coll-of ::operation))
|
||||
|
||||
(defmulti change-spec-impl :type)
|
||||
|
||||
(defmethod change-spec-impl :add-shape [_]
|
||||
(s/keys :req-un [::shape ::id ::session-id]
|
||||
:opt-un [::index]))
|
||||
(defmethod change-spec-impl :add-obj [_]
|
||||
(s/keys :req-un [::id ::frame-id ::obj]
|
||||
:opt-un [::session-id]))
|
||||
|
||||
(defmethod change-spec-impl :add-canvas [_]
|
||||
(s/keys :req-un [::shape ::id ::session-id]
|
||||
:opt-un [::index]))
|
||||
(defmethod change-spec-impl :mod-obj [_]
|
||||
(s/keys :req-un [::id ::operations]
|
||||
:opt-un [::session-id]))
|
||||
|
||||
(defmethod change-spec-impl :mod-shape [_]
|
||||
(s/keys :req-un [::id ::operations ::session-id]))
|
||||
(defmethod change-spec-impl :del-obj [_]
|
||||
(s/keys :req-un [::id]
|
||||
:opt-un [::session-id]))
|
||||
|
||||
(defmethod change-spec-impl :mov-shape [_]
|
||||
(s/keys :req-un [::id ::index ::session-id]))
|
||||
(defmethod change-spec-impl :mov-obj [_]
|
||||
(s/keys :req-un [::id ::frame-id]
|
||||
:opt-un [::session-id]))
|
||||
|
||||
(defmethod change-spec-impl :mod-opts [_]
|
||||
(s/keys :req-un [::operations ::session-id]))
|
||||
;; (defmethod change-spec-impl :mod-shape [_]
|
||||
;; (s/keys :req-un [::id ::operations ::session-id]))
|
||||
|
||||
(defmethod change-spec-impl :del-shape [_]
|
||||
(s/keys :req-un [::id ::session-id]))
|
||||
;; (defmethod change-spec-impl :mov-shape [_]
|
||||
;; (s/keys :req-un [::id ::index ::session-id]))
|
||||
|
||||
(defmethod change-spec-impl :del-canvas [_]
|
||||
(s/keys :req-un [::id ::session-id]))
|
||||
;; (defmethod change-spec-impl :mod-opts [_]
|
||||
;; (s/keys :req-un [::operations ::session-id]))
|
||||
|
||||
;; (defmethod change-spec-impl :del-shape [_]
|
||||
;; (s/keys :req-un [::id ::session-id]))
|
||||
|
||||
;; (defmethod change-spec-impl :del-canvas [_]
|
||||
;; (s/keys :req-un [::id ::session-id]))
|
||||
|
||||
(s/def ::change (s/multi-spec change-spec-impl :type))
|
||||
(s/def ::changes (s/coll-of ::change))
|
||||
|
||||
(def root #uuid "00000000-0000-0000-0000-000000000000")
|
||||
|
||||
(def default-page-data
|
||||
"A reference value of the empty page data."
|
||||
{:version 1
|
||||
:shapes []
|
||||
:canvas []
|
||||
{:version 3
|
||||
:options {}
|
||||
:shapes-by-id {}})
|
||||
:objects
|
||||
{root
|
||||
{:id root
|
||||
:type :frame
|
||||
:name "root"
|
||||
:shapes []}}})
|
||||
|
||||
;; --- Changes Processing Impl
|
||||
|
||||
(declare process-change)
|
||||
(declare process-mod-shape)
|
||||
(declare process-mod-opts)
|
||||
(declare process-mov-shape)
|
||||
(declare process-add-shape)
|
||||
(declare process-add-canvas)
|
||||
(declare process-del-shape)
|
||||
(declare process-del-canvas)
|
||||
(defmulti process-change
|
||||
(fn [data change] (:type change)))
|
||||
|
||||
(defn process-changes
|
||||
[data items]
|
||||
(->> (us/verify ::changes items)
|
||||
(reduce process-change data)))
|
||||
(reduce #(or (process-change %1 %2) %1) data)))
|
||||
|
||||
(defn- process-change
|
||||
[data {:keys [type] :as change}]
|
||||
(case type
|
||||
:add-shape (process-add-shape data change)
|
||||
:add-canvas (process-add-canvas data change)
|
||||
:mod-shape (process-mod-shape data change)
|
||||
:mov-shape (process-mov-shape data change)
|
||||
:del-shape (process-del-shape data change)
|
||||
:del-canvas (process-del-canvas data change)
|
||||
:mod-opts (process-mod-opts data change)))
|
||||
(defmethod process-change :add-obj
|
||||
[data {:keys [id obj frame-id index] :as change}]
|
||||
(us/assert! (contains? (:objects data) frame-id) "process-change/add-obj")
|
||||
(let [obj (assoc obj
|
||||
:frame-id frame-id
|
||||
:id id)]
|
||||
(-> data
|
||||
(update :objects assoc id obj)
|
||||
(update-in [:objects frame-id :shapes]
|
||||
(fn [shapes]
|
||||
(cond
|
||||
(some #{id} shapes)
|
||||
shapes
|
||||
|
||||
(defn- process-add-shape
|
||||
[data {:keys [id index shape] :as change}]
|
||||
(-> data
|
||||
(update :shapes (fn [shapes]
|
||||
(cond
|
||||
(some #{id} shapes)
|
||||
shapes
|
||||
(nil? index)
|
||||
(conj shapes id)
|
||||
|
||||
(nil? index)
|
||||
(conj shapes id)
|
||||
:else
|
||||
(let [[before after] (split-at index shapes)]
|
||||
(d/concat [] before [id] after))))))))
|
||||
|
||||
:else
|
||||
(let [[before after] (split-at index shapes)]
|
||||
(d/concat [] before [id] after)))))
|
||||
(update :shapes-by-id assoc id shape)))
|
||||
(defn- process-obj-operation
|
||||
[shape op]
|
||||
(case (:type op)
|
||||
:set
|
||||
(let [attr (:attr op)
|
||||
val (:val op)]
|
||||
(if (nil? val)
|
||||
(dissoc shape attr)
|
||||
(assoc shape attr val)))
|
||||
|
||||
(defn- process-add-canvas
|
||||
[data {:keys [id shape index] :as change}]
|
||||
(-> data
|
||||
(update :canvas (fn [shapes]
|
||||
(cond
|
||||
(some #{id} shapes)
|
||||
shapes
|
||||
(ex/raise :type :not-implemented
|
||||
:hint "TODO")))
|
||||
|
||||
(nil? index)
|
||||
(conj shapes id)
|
||||
|
||||
:else
|
||||
(let [[before after] (split-at index shapes)]
|
||||
(d/concat [] before [id] after)))))
|
||||
|
||||
(update :shapes-by-id assoc id shape)))
|
||||
|
||||
(defn- process-mod-shape
|
||||
(defmethod process-change :mod-obj
|
||||
[data {:keys [id operations] :as change}]
|
||||
(if (get-in data [:shapes-by-id id])
|
||||
(update-in data [:shapes-by-id id]
|
||||
#(reduce (fn [shape [_ att val]]
|
||||
(if (nil? val)
|
||||
(dissoc shape att)
|
||||
(assoc shape att val)))
|
||||
% operations))
|
||||
data))
|
||||
(us/assert! (contains? (:objects data) id) "process-change/mod-obj")
|
||||
(update-in data [:objects id]
|
||||
#(reduce process-obj-operation % operations)))
|
||||
|
||||
(defn- process-mod-opts
|
||||
[data {:keys [operations]}]
|
||||
(update data :options
|
||||
#(reduce (fn [options [_ att val]]
|
||||
(if (nil? val)
|
||||
(dissoc options att)
|
||||
(assoc options att val)))
|
||||
% operations)))
|
||||
(defmethod process-change :mov-obj
|
||||
[data {:keys [id frame-id] :as change}]
|
||||
(us/assert! (contains? (:objects data) frame-id))
|
||||
(let [frame-id' (get-in data [:objects id :frame-id])]
|
||||
(when (not= frame-id frame-id')
|
||||
(-> data
|
||||
(update-in [:objects frame-id' :shapes] (fn [s] (filterv #(not= % id) s)))
|
||||
(update-in [:objects id] assoc :frame-id frame-id)
|
||||
(update-in [:objects frame-id :shapes] conj id)))))
|
||||
|
||||
(defn- process-mov-shape
|
||||
[data {:keys [id index]}]
|
||||
(let [shapes (:shapes data)
|
||||
current-index (d/index-of shapes id)
|
||||
shapes' (into [] (remove #(= % id) shapes))]
|
||||
(cond
|
||||
(= index current-index)
|
||||
data
|
||||
|
||||
(nil? current-index)
|
||||
(assoc data :shapes (d/concat [id] shapes'))
|
||||
|
||||
:else
|
||||
(let [[before after] (split-at index shapes')]
|
||||
(assoc data :shapes (d/concat [] before [id] after))))))
|
||||
|
||||
(defn- process-del-shape
|
||||
(defmethod process-change :del-obj
|
||||
[data {:keys [id] :as change}]
|
||||
(-> data
|
||||
(update :shapes (fn [s] (filterv #(not= % id) s)))
|
||||
(update :shapes-by-id dissoc id)))
|
||||
(when-let [{:keys [frame-id] :as obj} (get-in data [:objects id])]
|
||||
(-> data
|
||||
(update :objects dissoc id)
|
||||
(update-in [:objects frame-id :shapes]
|
||||
(fn [s] (filterv #(not= % id) s))))))
|
||||
|
||||
(defn- process-del-canvas
|
||||
[data {:keys [id] :as change}]
|
||||
(-> data
|
||||
(update :canvas (fn [s] (filterv #(not= % id) s)))
|
||||
(update :shapes-by-id dissoc id)))
|
||||
;; (defn- process-change
|
||||
;; [data {:keys [type] :as change}]
|
||||
;; (case type
|
||||
;; :add-obj (process-add-obj data change)
|
||||
;; :mod-obj (process-mod-obj data change)
|
||||
|
||||
;; ;; :add-shape (process-add-shape data change)
|
||||
;; ;; :add-canvas (process-add-canvas data change)
|
||||
;; ;; :mod-shape (process-mod-shape data change)
|
||||
;; ;; :mov-shape (process-mov-shape data change)
|
||||
;; ;; :del-shape (process-del-shape data change)
|
||||
;; ;; :del-canvas (process-del-canvas data change)
|
||||
;; ;; :mod-opts (process-mod-opts data change)
|
||||
;; ))
|
||||
|
||||
;; (defn- process-add-obj
|
||||
|
||||
|
||||
;; (defn- process-add-shape
|
||||
;; [data {:keys [id index shape] :as change}]
|
||||
;; (-> data
|
||||
;; (update :shapes (fn [shapes]
|
||||
;; (cond
|
||||
;; (some #{id} shapes)
|
||||
;; shapes
|
||||
|
||||
;; (nil? index)
|
||||
;; (conj shapes id)
|
||||
|
||||
;; :else
|
||||
;; (let [[before after] (split-at index shapes)]
|
||||
;; (d/concat [] before [id] after)))))
|
||||
;; (update :shapes-by-id assoc id shape)))
|
||||
|
||||
;; (defn- process-add-canvas
|
||||
;; [data {:keys [id shape index] :as change}]
|
||||
;; (-> data
|
||||
;; (update :canvas (fn [shapes]
|
||||
;; (cond
|
||||
;; (some #{id} shapes)
|
||||
;; shapes
|
||||
|
||||
;; (nil? index)
|
||||
;; (conj shapes id)
|
||||
|
||||
;; :else
|
||||
;; (let [[before after] (split-at index shapes)]
|
||||
;; (d/concat [] before [id] after)))))
|
||||
|
||||
;; (update :shapes-by-id assoc id shape)))
|
||||
|
||||
;; (defn- process-mod-shape
|
||||
;; [data {:keys [id operations] :as change}]
|
||||
;; (if (get-in data [:shapes-by-id id])
|
||||
;; (update-in data [:shapes-by-id id]
|
||||
;; #(reduce (fn [shape [_ att val]]
|
||||
;; (if (nil? val)
|
||||
;; (dissoc shape att)
|
||||
;; (assoc shape att val)))
|
||||
;; % operations))
|
||||
;; data))
|
||||
|
||||
;; (defn- process-mod-opts
|
||||
;; [data {:keys [operations]}]
|
||||
;; (update data :options
|
||||
;; #(reduce (fn [options [_ att val]]
|
||||
;; (if (nil? val)
|
||||
;; (dissoc options att)
|
||||
;; (assoc options att val)))
|
||||
;; % operations)))
|
||||
|
||||
;; (defn- process-mov-shape
|
||||
;; [data {:keys [id index]}]
|
||||
;; (let [shapes (:shapes data)
|
||||
;; current-index (d/index-of shapes id)
|
||||
;; shapes' (into [] (remove #(= % id) shapes))]
|
||||
;; (cond
|
||||
;; (= index current-index)
|
||||
;; data
|
||||
|
||||
;; (nil? current-index)
|
||||
;; (assoc data :shapes (d/concat [id] shapes'))
|
||||
|
||||
;; :else
|
||||
;; (let [[before after] (split-at index shapes')]
|
||||
;; (assoc data :shapes (d/concat [] before [id] after))))))
|
||||
|
||||
;; (defn- process-del-shape
|
||||
;; [data {:keys [id] :as change}]
|
||||
;; (-> data
|
||||
;; (update :shapes (fn [s] (filterv #(not= % id) s)))
|
||||
;; (update :shapes-by-id dissoc id)))
|
||||
|
||||
;; (defn- process-del-canvas
|
||||
;; [data {:keys [id] :as change}]
|
||||
;; (-> data
|
||||
;; (update :canvas (fn [s] (filterv #(not= % id) s)))
|
||||
;; (update :shapes-by-id dissoc id)))
|
||||
|
||||
|
|
|
@ -110,6 +110,21 @@
|
|||
|
||||
;; --- Macros
|
||||
|
||||
(defmacro assert!
|
||||
"Evaluates expr and throws an exception if it does not evaluate to
|
||||
logical true."
|
||||
([x]
|
||||
(when *assert*
|
||||
`(when-not ~x
|
||||
(throw (ex/error :type :assertion-error
|
||||
:hint (str "Assert failed: " (pr-str '~x)))))))
|
||||
([x message]
|
||||
(when *assert*
|
||||
`(when-not ~x
|
||||
(throw (ex/error :type :assertion-error
|
||||
:hint (str "Assert failed: " (pr-str '~x))
|
||||
:message ~message))))))
|
||||
|
||||
(defn spec-assert
|
||||
[spec x]
|
||||
(s/assert* spec x))
|
||||
|
|
|
@ -9,35 +9,64 @@ and persistence protocol.
|
|||
This is a page data structure:
|
||||
|
||||
```
|
||||
{:version 1
|
||||
{:version 2
|
||||
:options {}
|
||||
:shapes [<id>, ...]
|
||||
:canvas [<id>, ...]
|
||||
:shapes-by-id {<id> <object>, ...}}
|
||||
|
||||
:rmap
|
||||
{:id1 :default
|
||||
:id2 :default
|
||||
:id3 :id1}
|
||||
|
||||
:objects
|
||||
{:root
|
||||
{:type :root
|
||||
:shapes [:id1 :id2]}
|
||||
|
||||
:id1
|
||||
{:type :canvas
|
||||
:shapes [:id3]}
|
||||
|
||||
:id2 {:type :rect}
|
||||
:id3 {:type :circle}}}
|
||||
```
|
||||
|
||||
|
||||
This is a potential list of persistent ops:
|
||||
|
||||
```
|
||||
;; Generic (Shapes & Canvas)
|
||||
[:mod-shape <id> [:set <attr> <val?>], ...]
|
||||
{:type :mod-opts
|
||||
:operations [<op>, ...]
|
||||
|
||||
;; Example:
|
||||
;; [:mod-shape 1 [:set :x 2] [:set :y 3]]
|
||||
{:type :add-obj
|
||||
:id <uuid>
|
||||
:parent <uuid>
|
||||
:obj <shape-object>}
|
||||
|
||||
;; Specific
|
||||
[:add-shape <id> <object>]
|
||||
[:add-canvas <id> <object>]
|
||||
{:type :mod-obj
|
||||
:id <uuid>
|
||||
:operations [<op>, ...]}
|
||||
|
||||
[:del-shape <id>]
|
||||
[:del-canvas <id>]
|
||||
{:type :mov-obj
|
||||
:id <uuid>
|
||||
:dest <uuid>}
|
||||
|
||||
[:mov-canvas <id> :after <id|null>] ;; null implies at first position
|
||||
[:mov-shape <id> :after <id|null>]
|
||||
|
||||
[:mod-opts [:set <attr> <val>], [:del <attr> nil], ...]
|
||||
{:type :del-obj
|
||||
:id <uuid>}
|
||||
```
|
||||
|
||||
This is a potential list of operations:
|
||||
|
||||
```
|
||||
{:type :set
|
||||
:attr <any>
|
||||
:val <any>}
|
||||
|
||||
{:type :mov
|
||||
:id <uuid>
|
||||
:index <int>}
|
||||
```
|
||||
|
||||
|
||||
## Ephemeral communication (Websocket protocol)
|
||||
|
||||
|
||||
|
|
|
@ -3,9 +3,9 @@
|
|||
org.clojure/clojure {:mvn/version "1.10.1"}
|
||||
com.cognitect/transit-cljs {:mvn/version "0.8.256"}
|
||||
|
||||
cljsjs/react {:mvn/version "16.12.0-1"}
|
||||
cljsjs/react-dom {:mvn/version "16.12.0-1"}
|
||||
cljsjs/react-dom-server {:mvn/version "16.12.0-1"}
|
||||
cljsjs/react {:mvn/version "16.13.0-0"}
|
||||
cljsjs/react-dom {:mvn/version "16.13.0-0"}
|
||||
cljsjs/react-dom-server {:mvn/version "16.13.0-0"}
|
||||
|
||||
environ/environ {:mvn/version "1.1.0"}
|
||||
metosin/reitit-core {:mvn/version "0.3.10"}
|
||||
|
|
|
@ -9,8 +9,8 @@
|
|||
(def viewport-width 4000)
|
||||
(def viewport-height 4000)
|
||||
|
||||
(def canvas-start-x 1200)
|
||||
(def canvas-start-y 1200)
|
||||
(def frame-start-x 1200)
|
||||
(def frame-start-y 1200)
|
||||
|
||||
(def grid-x-axis 10)
|
||||
(def grid-y-axis 10)
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -11,7 +11,7 @@
|
|||
[rumext.alpha :as mf]
|
||||
[uxbox.util.math :as mth]
|
||||
[uxbox.main.geom :as geom]
|
||||
[uxbox.main.ui.shapes.canvas :as canvas]
|
||||
[uxbox.main.ui.shapes.frame :as frame]
|
||||
[uxbox.main.ui.shapes.circle :as circle]
|
||||
[uxbox.main.ui.shapes.icon :as icon]
|
||||
[uxbox.main.ui.shapes.image :as image]
|
||||
|
@ -40,7 +40,7 @@
|
|||
[{:keys [shape] :as props}]
|
||||
(when (and shape (not (:hidden shape)))
|
||||
(case (:type shape)
|
||||
:canvas [:& rect/rect-shape {:shape shape}]
|
||||
:frame [:& rect/rect-shape {:shape shape}]
|
||||
:curve [:& path/path-shape {:shape shape}]
|
||||
:text [:& text/text-shape {:shape shape}]
|
||||
:icon [:& icon/icon-shape {:shape shape}]
|
||||
|
@ -53,7 +53,7 @@
|
|||
[{:keys [data] :as props}]
|
||||
(let [shapes-by-id (:shapes-by-id data)
|
||||
shapes (map #(get shapes-by-id %) (:shapes data []))
|
||||
canvas (map #(get shapes-by-id %) (:canvas data []))
|
||||
frame (map #(get shapes-by-id %) (:frame data []))
|
||||
dim (calculate-dimensions data)]
|
||||
[:svg {:view-box (str "0 0 " (:width dim 0) " " (:height dim 0))
|
||||
:version "1.1"
|
||||
|
@ -61,7 +61,7 @@
|
|||
:xmlns "http://www.w3.org/2000/svg"}
|
||||
(background)
|
||||
[:*
|
||||
(for [item canvas]
|
||||
(for [item frame]
|
||||
[:& shape-wrapper {:shape item :key (:id item)}])
|
||||
(for [item shapes]
|
||||
[:& shape-wrapper {:shape item :key (:id item)}])]]))
|
||||
|
|
|
@ -27,7 +27,7 @@
|
|||
:icon (move-rect shape dpoint)
|
||||
:image (move-rect shape dpoint)
|
||||
:rect (move-rect shape dpoint)
|
||||
:canvas (move-rect shape dpoint)
|
||||
:frame (move-rect shape dpoint)
|
||||
:text (move-rect shape dpoint)
|
||||
:curve (move-path shape dpoint)
|
||||
:path (move-path shape dpoint)
|
||||
|
@ -69,7 +69,7 @@
|
|||
[shape position]
|
||||
(case (:type shape)
|
||||
:icon (absolute-move-rect shape position)
|
||||
:canvas (absolute-move-rect shape position)
|
||||
:frame (absolute-move-rect shape position)
|
||||
:image (absolute-move-rect shape position)
|
||||
:rect (absolute-move-rect shape position)
|
||||
:circle (absolute-move-circle shape position)))
|
||||
|
@ -482,6 +482,25 @@
|
|||
:width width
|
||||
:height height)))
|
||||
|
||||
;; --- Resolve Shape
|
||||
|
||||
(declare resolve-rect-shape)
|
||||
(declare translate-from-frame)
|
||||
(declare translate-to-frame)
|
||||
|
||||
(defn resolve-shape
|
||||
[objects shape]
|
||||
(case (:type shape)
|
||||
:rect (resolve-rect-shape objects shape)
|
||||
:frame (resolve-rect-shape objects shape)))
|
||||
|
||||
(defn- resolve-rect-shape
|
||||
[objects {:keys [parent] :as shape}]
|
||||
(loop [pobj (get objects parent)]
|
||||
(if (= :frame (:type pobj))
|
||||
(translate-from-frame shape pobj)
|
||||
(recur (get objects (:parent pobj))))))
|
||||
|
||||
;; --- Transform Shape
|
||||
|
||||
(declare transform-rect)
|
||||
|
@ -492,7 +511,7 @@
|
|||
"Apply the matrix transformation to shape."
|
||||
[{:keys [type] :as shape} xfmt]
|
||||
(case type
|
||||
:canvas (transform-rect shape xfmt)
|
||||
:frame (transform-rect shape xfmt)
|
||||
:rect (transform-rect shape xfmt)
|
||||
:icon (transform-rect shape xfmt)
|
||||
:text (transform-rect shape xfmt)
|
||||
|
@ -599,6 +618,14 @@
|
|||
:height (- maxy miny)
|
||||
:type :rect}))
|
||||
|
||||
(defn translate-to-frame
|
||||
[shape {:keys [x y] :as frame}]
|
||||
(move shape (gpt/point (- x) (- y))))
|
||||
|
||||
(defn translate-from-frame
|
||||
[shape {:keys [x y] :as frame}]
|
||||
(move shape (gpt/point (+ x) (+ y))))
|
||||
|
||||
;; --- Helpers
|
||||
|
||||
(defn contained-in?
|
||||
|
|
|
@ -59,8 +59,8 @@
|
|||
(-> (l/lens #(contains? % id))
|
||||
(l/derive selected-shapes)))
|
||||
|
||||
(def selected-canvas
|
||||
(-> (l/key :selected-canvas)
|
||||
(def selected-frame
|
||||
(-> (l/key :selected-frame)
|
||||
(l/derive workspace-local)))
|
||||
|
||||
(def toolboxes
|
||||
|
|
|
@ -13,7 +13,7 @@
|
|||
[rumext.alpha :as mf]
|
||||
[uxbox.main.refs :as refs]
|
||||
[uxbox.main.store :as st]
|
||||
[uxbox.main.ui.shapes.canvas :as canvas]))
|
||||
[uxbox.main.ui.shapes.frame :as frame]))
|
||||
|
||||
(def shape-wrapper canvas/shape-wrapper)
|
||||
(def canvas-wrapper canvas/canvas-wrapper)
|
||||
(def shape-wrapper frame/shape-wrapper)
|
||||
(def frame-wrapper frame/frame-wrapper)
|
||||
|
|
|
@ -5,7 +5,9 @@
|
|||
;; Copyright (c) 2016-2017 Andrey Antukh <niwi@niwi.nz>
|
||||
|
||||
(ns uxbox.main.ui.shapes.attrs
|
||||
(:require [cuerdas.core :as str]))
|
||||
(:require
|
||||
[cuerdas.core :as str]
|
||||
[uxbox.util.interop :as interop]))
|
||||
|
||||
|
||||
;; (defn camel-case
|
||||
|
@ -58,7 +60,8 @@
|
|||
(case style
|
||||
:mixed "5,5,1,5"
|
||||
:dotted "5,5"
|
||||
:dashed "10,10"))
|
||||
:dashed "10,10"
|
||||
nil))
|
||||
|
||||
(defn- transform-stroke-attrs
|
||||
[{:keys [stroke-style] :or {stroke-style :none} :as attrs}]
|
||||
|
@ -75,3 +78,21 @@
|
|||
(-> (select-keys shape shape-style-attrs)
|
||||
(transform-stroke-attrs)
|
||||
(process-attrs)))
|
||||
|
||||
|
||||
;; TODO: migrate all the code to use this function and then, rename.
|
||||
|
||||
(defn extract-style-attrs2
|
||||
[shape]
|
||||
(let [stroke-style (:stroke-style shape :none)
|
||||
attrs #js {:fill (:fill-color shape nil)
|
||||
:opacity (:opacity shape nil)
|
||||
:rx (:rx shape nil)
|
||||
:ry (:ry shape nil)}]
|
||||
(when (not= :none stroke-style)
|
||||
(interop/obj-assign! attrs
|
||||
#js {:stroke (:stroke-color shape nil)
|
||||
:strokeWidth (:stroke-width shape nil)
|
||||
:strokeOpacity (:stroke-opacity shape nil)
|
||||
:strokeDasharray (stroke-type->dasharray stroke-style)}))
|
||||
attrs))
|
||||
|
|
|
@ -43,21 +43,21 @@
|
|||
(rx/of (dw/materialize-displacement-in-bulk selected)
|
||||
::dw/page-data-update))))))
|
||||
|
||||
(def start-move-canvas
|
||||
(def start-move-frame
|
||||
(ptk/reify ::start-move-selected
|
||||
ptk/WatchEvent
|
||||
(watch [_ state stream]
|
||||
(let [flags (get-in state [:workspace-local :flags])
|
||||
selected (get-in state [:workspace-local :selected])
|
||||
stoper (rx/filter uws/mouse-up? stream)
|
||||
canvas-id (first selected)
|
||||
frame-id (first selected)
|
||||
position @uws/mouse-position]
|
||||
|
||||
(rx/concat
|
||||
(->> (uws/mouse-position-deltas position)
|
||||
(rx/map #(dw/apply-canvas-displacement canvas-id %))
|
||||
(rx/map #(dw/apply-frame-displacement frame-id %))
|
||||
(rx/take-until stoper))
|
||||
(rx/of (dw/materialize-canvas-displacement canvas-id)))))))
|
||||
(rx/of (dw/materialize-frame-displacement frame-id)))))))
|
||||
|
||||
(defn on-mouse-down
|
||||
([event shape] (on-mouse-down event shape nil))
|
||||
|
@ -70,10 +70,10 @@
|
|||
drawing?
|
||||
nil
|
||||
|
||||
(= type :canvas)
|
||||
(= type :frame)
|
||||
(when selected?
|
||||
(dom/stop-propagation event)
|
||||
(st/emit! start-move-canvas))
|
||||
(st/emit! start-move-frame))
|
||||
|
||||
(and (not selected?) (empty? selected))
|
||||
(do
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
;; Copyright (c) 2015-2020 Andrey Antukh <niwi@niwi.nz>
|
||||
;; Copyright (c) 2015-2020 Juan de la Cruz <delacruzgarciajuan@gmail.com>
|
||||
|
||||
(ns uxbox.main.ui.shapes.canvas
|
||||
(ns uxbox.main.ui.shapes.frame
|
||||
(:require
|
||||
[lentes.core :as l]
|
||||
[rumext.alpha :as mf]
|
||||
|
@ -30,14 +30,24 @@
|
|||
[uxbox.util.geom.matrix :as gmt]
|
||||
[uxbox.util.geom.point :as gpt]))
|
||||
|
||||
(declare canvas-wrapper)
|
||||
(declare frame-wrapper)
|
||||
|
||||
|
||||
(defn wrap-memo-shape
|
||||
([component]
|
||||
(js/React.memo
|
||||
component
|
||||
(fn [np op]
|
||||
(let [n-shape (aget np "shape")
|
||||
o-shape (aget op "shape")]
|
||||
(= n-shape o-shape))))))
|
||||
|
||||
(mf/defc shape-wrapper
|
||||
{:wrap [#(mf/wrap-memo % =)]}
|
||||
{:wrap [wrap-memo-shape]}
|
||||
[{:keys [shape] :as props}]
|
||||
(when (and shape (not (:hidden shape)))
|
||||
(case (:type shape)
|
||||
:canvas [:& canvas-wrapper {:shape shape :childs []}]
|
||||
:frame [:& frame-wrapper {:shape shape :childs []}]
|
||||
:curve [:& path/path-wrapper {:shape shape}]
|
||||
:text [:& text/text-wrapper {:shape shape}]
|
||||
:icon [:& icon/icon-wrapper {:shape shape}]
|
||||
|
@ -46,22 +56,48 @@
|
|||
:image [:& image/image-wrapper {:shape shape}]
|
||||
:circle [:& circle/circle-wrapper {:shape shape}])))
|
||||
|
||||
(def canvas-default-props
|
||||
(def frame-default-props
|
||||
{:fill-color "#ffffff"})
|
||||
|
||||
(declare canvas-shape)
|
||||
(declare translate-to-canvas)
|
||||
(declare frame-shape)
|
||||
(declare translate-to-frame)
|
||||
|
||||
(mf/defc canvas-wrapper
|
||||
{:wrap [#(mf/wrap-memo % =)]}
|
||||
[{:keys [shape childs] :as props}]
|
||||
(def kaka [1 2 3])
|
||||
|
||||
(defn wrap-memo-frame
|
||||
([component]
|
||||
(js/React.memo
|
||||
component
|
||||
(fn [np op]
|
||||
(let [n-shape (aget np "shape")
|
||||
o-shape (aget op "shape")
|
||||
n-objs (aget np "objects")
|
||||
o-objs (aget op "objects")
|
||||
|
||||
ids (:shapes n-shape)]
|
||||
(and (identical? n-shape o-shape)
|
||||
(loop [id (first ids)
|
||||
ids (rest ids)]
|
||||
(if (nil? id)
|
||||
true
|
||||
(if (identical? (get n-objs id)
|
||||
(get o-objs id))
|
||||
(recur (first ids) (rest ids))
|
||||
false)))))))))
|
||||
|
||||
|
||||
(mf/defc frame-wrapper
|
||||
{:wrap [wrap-memo-frame]}
|
||||
[{:keys [shape objects] :as props}]
|
||||
(when (and shape (not (:hidden shape)))
|
||||
(let [selected-iref (mf/use-memo
|
||||
{:fn #(refs/make-selected (:id shape))
|
||||
:deps (mf/deps (:id shape))})
|
||||
selected? (mf/deref selected-iref)
|
||||
on-mouse-down #(common/on-mouse-down % shape)
|
||||
shape (merge canvas-default-props shape)
|
||||
shape (merge frame-default-props shape)
|
||||
|
||||
childs (mapv #(get objects %) (:shapes shape))
|
||||
|
||||
on-double-click
|
||||
(fn [event]
|
||||
|
@ -71,9 +107,9 @@
|
|||
[:g {:class (when selected? "selected")
|
||||
:on-double-click on-double-click
|
||||
:on-mouse-down on-mouse-down}
|
||||
[:& canvas-shape {:shape shape :childs childs}]])))
|
||||
[:& frame-shape {:shape shape :childs childs}]])))
|
||||
|
||||
(mf/defc canvas-shape
|
||||
(mf/defc frame-shape
|
||||
[{:keys [shape childs] :as props}]
|
||||
(let [rotation (:rotation shape)
|
||||
ds-modifier (:displacement-modifier shape)
|
||||
|
@ -93,7 +129,7 @@
|
|||
:height height
|
||||
))
|
||||
|
||||
translate #(translate-to-canvas % ds-modifier (gpt/point (- x) (- y)))
|
||||
translate #(translate-to-frame % ds-modifier (gpt/point (- x) (- y)))
|
||||
]
|
||||
|
||||
[:svg {:x x :y y :width width :height height}
|
||||
|
@ -101,12 +137,12 @@
|
|||
(for [item childs]
|
||||
[:& shape-wrapper {:shape (translate item) :key (:id item)}])]))
|
||||
|
||||
(defn- translate-to-canvas
|
||||
[shape canvas-ds-modifier pt]
|
||||
(defn- translate-to-frame
|
||||
[shape frame-ds-modifier pt]
|
||||
(let [rz-modifier (:resize-modifier shape)
|
||||
shape (cond-> shape
|
||||
(gmt/matrix? canvas-ds-modifier)
|
||||
(geom/transform canvas-ds-modifier)
|
||||
(gmt/matrix? frame-ds-modifier)
|
||||
(geom/transform frame-ds-modifier)
|
||||
|
||||
(gmt/matrix? rz-modifier)
|
||||
(-> (geom/transform rz-modifier)
|
|
@ -12,8 +12,7 @@
|
|||
[uxbox.main.refs :as refs]
|
||||
[uxbox.main.ui.shapes.attrs :as attrs]
|
||||
[uxbox.main.ui.shapes.common :as common]
|
||||
[uxbox.util.data :refer [classnames normalize-props]]
|
||||
[uxbox.util.dom :as dom]
|
||||
[uxbox.util.interop :as interop]
|
||||
[uxbox.util.geom.matrix :as gmt]
|
||||
[uxbox.util.geom.point :as gpt]))
|
||||
|
||||
|
@ -21,23 +20,19 @@
|
|||
|
||||
(declare rect-shape)
|
||||
|
||||
(mf/defc rect-wrapper
|
||||
{:wrap [#(mf/wrap-memo % =)]}
|
||||
[{:keys [shape] :as props}]
|
||||
(let [selected-iref (mf/use-memo
|
||||
{:fn #(refs/make-selected (:id shape))
|
||||
:deps (mf/deps (:id shape))})
|
||||
selected? (mf/deref selected-iref)
|
||||
(mf/defrc rect-wrapper
|
||||
[props]
|
||||
(let [shape (unchecked-get props "shape")
|
||||
on-mouse-down #(common/on-mouse-down % shape)]
|
||||
[:g.shape {:class (when selected? "selected")
|
||||
:on-mouse-down on-mouse-down}
|
||||
[:g.shape {:on-mouse-down on-mouse-down}
|
||||
[:& rect-shape {:shape shape}]]))
|
||||
|
||||
;; --- Rect Shape
|
||||
|
||||
(mf/defc rect-shape
|
||||
[{:keys [shape] :as props}]
|
||||
(let [ds-modifier (:displacement-modifier shape)
|
||||
(mf/defrc rect-shape
|
||||
[props]
|
||||
(let [shape (unchecked-get props "shape")
|
||||
ds-modifier (:displacement-modifier shape)
|
||||
rz-modifier (:resize-modifier shape)
|
||||
|
||||
shape (cond-> shape
|
||||
|
@ -52,12 +47,13 @@
|
|||
(+ x (/ width 2))
|
||||
(+ y (/ height 2))))
|
||||
|
||||
props (-> (attrs/extract-style-attrs shape)
|
||||
(assoc :x x
|
||||
:y y
|
||||
:transform transform
|
||||
:id (str "shape-" id)
|
||||
:width width
|
||||
:height height
|
||||
))]
|
||||
[:& "rect" props]))
|
||||
props (-> (attrs/extract-style-attrs2 shape)
|
||||
(interop/obj-assign!
|
||||
#js {:x x
|
||||
:y y
|
||||
:transform transform
|
||||
:id (str "shape-" id)
|
||||
:width width
|
||||
:height height}))]
|
||||
|
||||
[:> "rect" props]))
|
||||
|
|
|
@ -45,10 +45,10 @@
|
|||
(st/emit! (ms/->ScrollEvent (gpt/point left top)))))
|
||||
|
||||
(defn- on-wheel
|
||||
[event canvas]
|
||||
[event frame]
|
||||
(when (kbd/ctrl? event)
|
||||
(let [prev-zoom @refs/selected-zoom
|
||||
dom (mf/ref-node canvas)
|
||||
dom (mf/ref-node frame)
|
||||
scroll-position (scroll/get-current-position-absolute dom)
|
||||
mouse-point @ms/mouse-position]
|
||||
(dom/prevent-default event)
|
||||
|
@ -60,7 +60,7 @@
|
|||
|
||||
(mf/defc workspace-content
|
||||
[{:keys [page file flags] :as params}]
|
||||
(let [canvas (mf/use-ref nil)
|
||||
(let [frame (mf/use-ref nil)
|
||||
layout (mf/deref refs/workspace-layout)
|
||||
left-sidebar? (not (empty? (keep layout [:layers :sitemap
|
||||
:document-history])))
|
||||
|
@ -77,7 +77,7 @@
|
|||
[:section.workspace-content
|
||||
{:class classes
|
||||
:on-scroll on-scroll
|
||||
:on-wheel #(on-wheel % canvas)}
|
||||
:on-wheel #(on-wheel % frame)}
|
||||
|
||||
[:& history-dialog]
|
||||
|
||||
|
@ -87,7 +87,7 @@
|
|||
[:& horizontal-rule]
|
||||
[:& vertical-rule]])
|
||||
|
||||
[:section.workspace-viewport {:id "workspace-viewport" :ref canvas}
|
||||
[:section.workspace-viewport {:id "workspace-viewport" :ref frame}
|
||||
[:& viewport {:page page}]]]
|
||||
|
||||
;; Aside
|
||||
|
|
|
@ -52,7 +52,7 @@
|
|||
:fill-color "#000000"
|
||||
:fill-opacity 0
|
||||
:segments []}
|
||||
{:type :canvas
|
||||
{:type :frame
|
||||
:name "Canvas"}
|
||||
{:type :curve
|
||||
:name "Path"
|
||||
|
@ -281,8 +281,8 @@
|
|||
shape (dissoc shape ::initialized? :resize-modifier)]
|
||||
;; Add & select the created shape to the workspace
|
||||
(rx/of dw/deselect-all
|
||||
(if (= :canvas (:type shape))
|
||||
(dw/add-canvas shape)
|
||||
(if (= :frame (:type shape))
|
||||
(dw/add-frame shape)
|
||||
(dw/add-shape shape))))))))))
|
||||
|
||||
(def close-drawing-path
|
||||
|
|
|
@ -94,9 +94,9 @@
|
|||
[:div.workspace-options
|
||||
[:ul.options-btn
|
||||
[:li.tooltip.tooltip-bottom
|
||||
{:alt (tr "workspace.header.canvas")
|
||||
:class (when (= selected-drawtool :canvas) "selected")
|
||||
:on-click (partial select-drawtool :canvas)}
|
||||
{:alt (tr "workspace.header.frame")
|
||||
:class (when (= selected-drawtool :frame) "selected")
|
||||
:on-click (partial select-drawtool :frame)}
|
||||
i/artboard]
|
||||
[:li.tooltip.tooltip-bottom
|
||||
{:alt (tr "workspace.header.rect")
|
||||
|
|
|
@ -133,7 +133,7 @@
|
|||
:stroke-opacity "1"}}]
|
||||
|
||||
(when (and (fn? on-rotate)
|
||||
(not= :canvas (:type shape)))
|
||||
(not= :frame (:type shape)))
|
||||
[:*
|
||||
[:path {:stroke "#31EFB8"
|
||||
:stroke-opacity "1"
|
||||
|
@ -255,7 +255,7 @@
|
|||
:on-resize on-resize}]))
|
||||
|
||||
(mf/defc single-selection-handlers
|
||||
[{:keys [shape zoom] :as props}]
|
||||
[{:keys [shape zoom objects] :as props}]
|
||||
(let [on-resize #(do (dom/stop-propagation %2)
|
||||
(st/emit! (start-resize %1 #{(:id shape)} shape)))
|
||||
on-rotate #(do (dom/stop-propagation %)
|
||||
|
@ -263,6 +263,7 @@
|
|||
|
||||
ds-modifier (:displacement-modifier shape)
|
||||
rz-modifier (:resize-modifier shape)
|
||||
;; shape (geom/resolve-shape objects shape)
|
||||
shape (cond-> (geom/shape->rect-shape shape)
|
||||
(gmt/matrix? rz-modifier) (geom/transform rz-modifier)
|
||||
(gmt/matrix? ds-modifier) (geom/transform ds-modifier))]
|
||||
|
@ -274,11 +275,13 @@
|
|||
|
||||
(mf/defc selection-handlers
|
||||
[{:keys [selected edition zoom] :as props}]
|
||||
(let [data (mf/deref refs/workspace-data)
|
||||
(let [data (mf/deref refs/workspace-data)
|
||||
objects (:objects data)
|
||||
|
||||
;; We need remove posible nil values because on shape
|
||||
;; deletion many shape will reamin selected and deleted
|
||||
;; in the same time for small instant of time
|
||||
shapes (->> (map #(get-in data [:shapes-by-id %]) selected)
|
||||
shapes (->> (map #(get objects %) selected)
|
||||
(remove nil?))
|
||||
num (count shapes)
|
||||
{:keys [id type] :as shape} (first shapes)]
|
||||
|
@ -303,4 +306,5 @@
|
|||
|
||||
:else
|
||||
[:& single-selection-handlers {:shape shape
|
||||
:objects objects
|
||||
:zoom zoom}])))
|
||||
|
|
|
@ -21,6 +21,7 @@
|
|||
[uxbox.main.ui.shapes.icon :as icon]
|
||||
[uxbox.main.ui.workspace.sortable :refer [use-sortable]]
|
||||
[uxbox.util.dom :as dom]
|
||||
[uxbox.util.uuid :as uuid]
|
||||
[uxbox.util.i18n :as i18n :refer [t]]))
|
||||
|
||||
(def ^:private shapes-iref
|
||||
|
@ -76,34 +77,35 @@
|
|||
{:on-double-click on-click}
|
||||
(:name shape "")])))
|
||||
|
||||
;; --- Layer Item
|
||||
|
||||
(def strip-attrs
|
||||
#(select-keys % [:id :frame :name :type :hidden :blocked]))
|
||||
|
||||
(mf/defc layer-item
|
||||
{:wrap [#(mf/wrap-memo % =)]}
|
||||
[{:keys [shape selected index] :as props}]
|
||||
(let [selected? (contains? selected (:id shape))
|
||||
|
||||
{:wrap [mf/wrap-memo]}
|
||||
[{:keys [index item selected] :as props}]
|
||||
(let [selected? (contains? selected (:id item))
|
||||
toggle-blocking
|
||||
(fn [event]
|
||||
(dom/stop-propagation event)
|
||||
(if (:blocked shape)
|
||||
(st/emit! (dw/unblock-shape (:id shape)))
|
||||
(st/emit! (dw/block-shape (:id shape)))))
|
||||
(if (:blocked item)
|
||||
(st/emit! (dw/unblock-shape (:id item)))
|
||||
(st/emit! (dw/block-shape (:id item)))))
|
||||
|
||||
toggle-visibility
|
||||
(fn [event]
|
||||
(dom/stop-propagation event)
|
||||
(if (:hidden shape)
|
||||
(st/emit! (dw/show-shape (:id shape)))
|
||||
(st/emit! (dw/hide-shape (:id shape)))))
|
||||
(if (:hidden item)
|
||||
(st/emit! (dw/show-shape (:id item)))
|
||||
(st/emit! (dw/hide-shape (:id item)))))
|
||||
|
||||
select-shape
|
||||
(fn [event]
|
||||
(dom/prevent-default event)
|
||||
(let [id (:id shape)]
|
||||
(let [id (:id item)]
|
||||
(cond
|
||||
(or (:blocked shape)
|
||||
(:hidden shape))
|
||||
(or (:blocked item)
|
||||
(:hidden item))
|
||||
nil
|
||||
|
||||
(.-ctrlKey event)
|
||||
|
@ -118,7 +120,7 @@
|
|||
|
||||
on-drop
|
||||
(fn [item monitor]
|
||||
(st/emit! (dw/commit-shape-order-change (:shape-id item))))
|
||||
#_(st/emit! (dw/commit-shape-order-change (:shape-id item))))
|
||||
|
||||
on-hover
|
||||
(fn [item monitor]
|
||||
|
@ -126,8 +128,8 @@
|
|||
|
||||
[dprops dnd-ref] (use-sortable
|
||||
{:type "layer-item"
|
||||
:data {:shape-id (:id shape)
|
||||
:page-id (:page shape)
|
||||
:data {:shape-id (:id item)
|
||||
:page-id (:page item)
|
||||
:index index}
|
||||
:on-hover on-hover
|
||||
:on-drop on-drop})]
|
||||
|
@ -139,24 +141,22 @@
|
|||
:on-click select-shape
|
||||
:on-double-click #(dom/stop-propagation %)}
|
||||
[:div.element-actions
|
||||
[:div.toggle-element {:class (when-not (:hidden shape) "selected")
|
||||
[:div.toggle-element {:class (when-not (:hidden item) "selected")
|
||||
:on-click toggle-visibility}
|
||||
i/eye]
|
||||
[:div.block-element {:class (when (:blocked shape) "selected")
|
||||
[:div.block-element {:class (when (:blocked item) "selected")
|
||||
:on-click toggle-blocking}
|
||||
i/lock]]
|
||||
[:& element-icon {:shape shape}]
|
||||
[:& layer-name {:shape shape}]]]))
|
||||
[:& element-icon {:shape item}]
|
||||
[:& layer-name {:shape item}]]]))
|
||||
|
||||
(mf/defc canvas-item
|
||||
(mf/defc layer-frame-item
|
||||
{:wrap [#(mf/wrap-memo % =)]}
|
||||
[{:keys [canvas shapes selected index] :as props}]
|
||||
(let [selected? (contains? selected (:id canvas))
|
||||
[{:keys [item selected index objects] :as props}]
|
||||
(let [selected? (contains? selected (:id item))
|
||||
local (mf/use-state {:collapsed false})
|
||||
collapsed? (:collapsed @local)
|
||||
|
||||
shapes (filter #(= (:canvas (second %)) (:id canvas)) shapes)
|
||||
|
||||
toggle-collapse
|
||||
(fn [event]
|
||||
(dom/stop-propagation event)
|
||||
|
@ -165,24 +165,24 @@
|
|||
toggle-blocking
|
||||
(fn [event]
|
||||
(dom/stop-propagation event)
|
||||
(if (:blocked canvas)
|
||||
(st/emit! (dw/unblock-shape (:id canvas)))
|
||||
(st/emit! (dw/block-shape (:id canvas)))))
|
||||
(if (:blocked item)
|
||||
(st/emit! (dw/unblock-shape (:id item)))
|
||||
(st/emit! (dw/block-shape (:id item)))))
|
||||
|
||||
toggle-visibility
|
||||
(fn [event]
|
||||
(dom/stop-propagation event)
|
||||
(if (:hidden canvas)
|
||||
(st/emit! (dw/show-canvas (:id canvas)))
|
||||
(st/emit! (dw/hide-canvas (:id canvas)))))
|
||||
(if (:hidden item)
|
||||
(st/emit! (dw/show-frame (:id item)))
|
||||
(st/emit! (dw/hide-frame (:id item)))))
|
||||
|
||||
select-shape
|
||||
(fn [event]
|
||||
(dom/prevent-default event)
|
||||
(let [id (:id canvas)]
|
||||
(let [id (:id item)]
|
||||
(cond
|
||||
(or (:blocked canvas)
|
||||
(:hidden canvas))
|
||||
(or (:blocked item)
|
||||
(:hidden item))
|
||||
nil
|
||||
|
||||
(.-ctrlKey event)
|
||||
|
@ -201,13 +201,13 @@
|
|||
|
||||
on-hover
|
||||
(fn [item monitor]
|
||||
(st/emit! (dw/change-canvas-order {:id (:canvas-id item)
|
||||
(st/emit! (dw/change-frame-order {:id (:frame-id item)
|
||||
:index index})))
|
||||
|
||||
[dprops dnd-ref] (use-sortable
|
||||
{:type "canvas-item"
|
||||
:data {:canvas-id (:id canvas)
|
||||
:page-id (:page canvas)
|
||||
{:type "frame-item"
|
||||
:data {:frame-id (:id item)
|
||||
:page-id (:page item)
|
||||
:index index}
|
||||
:on-hover on-hover
|
||||
:on-drop on-drop})]
|
||||
|
@ -219,84 +219,68 @@
|
|||
:on-click select-shape
|
||||
:on-double-click #(dom/stop-propagation %)}
|
||||
[:div.element-actions
|
||||
[:div.toggle-element {:class (when-not (:hidden canvas) "selected")
|
||||
[:div.toggle-element {:class (when-not (:hidden item) "selected")
|
||||
:on-click toggle-visibility}
|
||||
i/eye]
|
||||
#_[:div.block-element {:class (when (:blocked canvas) "selected")
|
||||
#_[:div.block-element {:class (when (:blocked item) "selected")
|
||||
:on-click toggle-blocking}
|
||||
i/lock]]
|
||||
[:div.element-icon i/folder]
|
||||
[:& layer-name {:shape canvas}]
|
||||
[:& layer-name {:shape item}]
|
||||
[:span.toggle-content
|
||||
{:on-click toggle-collapse
|
||||
:class (when-not collapsed? "inverse")}
|
||||
i/arrow-slide]]
|
||||
(when-not collapsed?
|
||||
[:ul
|
||||
(for [[index shape] shapes]
|
||||
[:& layer-item {:shape shape
|
||||
:selected selected
|
||||
:index index
|
||||
:key (:id shape)}])])]))
|
||||
(for [[index id] (d/enumerate (:shapes item))]
|
||||
(let [item (get objects id)]
|
||||
(if (= (:type item) :frame)
|
||||
[:& layer-frame-item
|
||||
{:item item
|
||||
:key (:id item)
|
||||
:objects objects
|
||||
:index index}]
|
||||
[:& layer-item
|
||||
{:item item
|
||||
:index index
|
||||
:key (:id item)}])))])]))
|
||||
|
||||
;; --- Layers List
|
||||
|
||||
(mf/defc layers-list
|
||||
{:wrap [#(mf/wrap-memo % =)]}
|
||||
[{:keys [shapes selected] :as props}]
|
||||
[:ul.element-list
|
||||
(for [[index shape] shapes]
|
||||
[:& layer-item {:shape shape
|
||||
:selected selected
|
||||
:index index
|
||||
:key (:id shape)}])])
|
||||
|
||||
(mf/defc canvas-list
|
||||
{:wrap [#(mf/wrap-memo % =)]}
|
||||
[{:keys [shapes canvas selected] :as props}]
|
||||
[:ul.element-list
|
||||
(for [[index item] canvas]
|
||||
[:& canvas-item {:canvas item
|
||||
:shapes shapes
|
||||
:selected selected
|
||||
:index index
|
||||
:key (:id item)}])])
|
||||
(mf/defc layers-tree
|
||||
{:wrap [mf/wrap-memo]}
|
||||
[props]
|
||||
(let [selected (mf/deref refs/selected-shapes)
|
||||
data (mf/deref refs/workspace-data)
|
||||
objects (:objects data)
|
||||
root (get objects uuid/zero)]
|
||||
[:ul.element-list
|
||||
(for [[index id] (d/enumerate (:shapes root))]
|
||||
(let [item (get objects id)]
|
||||
(if (= (:type item) :frame)
|
||||
[:& layer-frame-item
|
||||
{:item item
|
||||
:key (:id item)
|
||||
:objects objects
|
||||
:index index}]
|
||||
[:& layer-item
|
||||
{:item item
|
||||
:index index
|
||||
:key (:id item)}])))]))
|
||||
|
||||
;; --- Layers Toolbox
|
||||
|
||||
;; NOTE: we need to consider using something like react window for
|
||||
;; only render visible items instead of all.
|
||||
|
||||
(mf/defc layers-toolbox
|
||||
{:wrap [mf/wrap-memo]}
|
||||
[{:keys [page] :as props}]
|
||||
(let [locale (i18n/use-locale)
|
||||
on-click #(st/emit! (dw/toggle-layout-flag :layers))
|
||||
|
||||
selected (mf/deref refs/selected-shapes)
|
||||
data (mf/deref refs/workspace-data)
|
||||
|
||||
shapes-map (:shapes-by-id data)
|
||||
strip #(select-keys % [:id :canvas :name :type :hidden :blocked])
|
||||
|
||||
canvas (->> (:canvas data)
|
||||
(map #(get shapes-map %))
|
||||
(map strip)
|
||||
(d/enumerate))
|
||||
|
||||
shapes (->> (:shapes data)
|
||||
(map #(get shapes-map %))
|
||||
(map strip))
|
||||
|
||||
all-shapes (d/enumerate shapes)
|
||||
unc-shapes (->> shapes
|
||||
(filter #(nil? (:canvas %)))
|
||||
(d/enumerate))]
|
||||
on-click #(st/emit! (dw/toggle-layout-flag :layers))]
|
||||
[:div#layers.tool-window
|
||||
[:div.tool-window-bar
|
||||
[:div.tool-window-icon i/layers]
|
||||
[:span (t locale "workspace.sidebar.layers")]
|
||||
#_[:div.tool-window-close {:on-click on-click} i/close]]
|
||||
[:div.tool-window-content
|
||||
[:& canvas-list {:canvas canvas
|
||||
:shapes all-shapes
|
||||
:selected selected}]
|
||||
[:& layers-list {:shapes unc-shapes
|
||||
:selected selected}]]]))
|
||||
[:& layers-tree]]]))
|
||||
|
|
|
@ -13,7 +13,7 @@
|
|||
[uxbox.main.data.workspace :as udw]
|
||||
[uxbox.main.store :as st]
|
||||
[uxbox.main.refs :as refs]
|
||||
[uxbox.main.ui.workspace.sidebar.options.canvas :as canvas]
|
||||
[uxbox.main.ui.workspace.sidebar.options.frame :as frame]
|
||||
[uxbox.main.ui.workspace.sidebar.options.rect :as rect]
|
||||
[uxbox.main.ui.workspace.sidebar.options.icon :as icon]
|
||||
[uxbox.main.ui.workspace.sidebar.options.circle :as circle]
|
||||
|
@ -29,7 +29,7 @@
|
|||
[{:keys [shape] :as props}]
|
||||
[:div
|
||||
(case (:type shape)
|
||||
:canvas [:& canvas/options {:shape shape}]
|
||||
:frame [:& frame/options {:shape shape}]
|
||||
:text [:& text/options {:shape shape}]
|
||||
:rect [:& rect/options {:shape shape}]
|
||||
:icon [:& icon/options {:shape shape}]
|
||||
|
@ -43,7 +43,7 @@
|
|||
[{:keys [shape-id] :as props}]
|
||||
(let [shape-iref (mf/use-memo
|
||||
{:deps (mf/deps shape-id)
|
||||
:fn #(-> (l/in [:workspace-data :shapes-by-id shape-id])
|
||||
:fn #(-> (l/in [:workspace-data :objects shape-id])
|
||||
(l/derive st/state))})
|
||||
shape (mf/deref shape-iref)]
|
||||
[:& shape-options {:shape shape}]))
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
;; Copyright (c) 2015-2020 Andrey Antukh <niwi@niwi.nz>
|
||||
;; Copyright (c) 2015-2020 Juan de la Cruz <delacruzgarciajuan@gmail.com>
|
||||
|
||||
(ns uxbox.main.ui.workspace.sidebar.options.canvas
|
||||
(ns uxbox.main.ui.workspace.sidebar.options.frame
|
||||
(:require
|
||||
[rumext.alpha :as mf]
|
||||
[uxbox.common.data :as d]
|
||||
|
@ -43,8 +43,8 @@
|
|||
delta (if (= attr :x)
|
||||
(gpt/point (math/neg (- pval cval)) 0)
|
||||
(gpt/point 0 (math/neg (- pval cval))))]
|
||||
(st/emit! (udw/apply-canvas-displacement (:id shape) delta)
|
||||
(udw/materialize-canvas-displacement (:id shape)))))
|
||||
(st/emit! (udw/apply-frame-displacement (:id shape) delta)
|
||||
(udw/materialize-frame-displacement (:id shape)))))
|
||||
|
||||
on-width-change #(on-size-change % :width)
|
||||
on-height-change #(on-size-change % :height)
|
|
@ -22,13 +22,13 @@
|
|||
[uxbox.main.ui.workspace.grid :refer [grid]]
|
||||
[uxbox.main.ui.workspace.ruler :refer [ruler]]
|
||||
[uxbox.main.ui.workspace.drawarea :refer [start-drawing]]
|
||||
|
||||
[uxbox.main.ui.shapes :refer [shape-wrapper canvas-wrapper]]
|
||||
[uxbox.main.ui.shapes :refer [shape-wrapper frame-wrapper]]
|
||||
[uxbox.main.ui.workspace.drawarea :refer [draw-area]]
|
||||
[uxbox.main.ui.workspace.selection :refer [selection-handlers]]
|
||||
|
||||
[uxbox.util.data :refer [parse-int]]
|
||||
[uxbox.util.perf :as perf]
|
||||
[uxbox.util.components :refer [use-rxsub]]
|
||||
[uxbox.util.uuid :as uuid]
|
||||
[uxbox.util.dom :as dom]
|
||||
[uxbox.util.geom.point :as gpt])
|
||||
(:import goog.events.EventType))
|
||||
|
@ -142,22 +142,23 @@
|
|||
|
||||
(declare remote-user-cursors)
|
||||
|
||||
(mf/defc canvas-and-shapes
|
||||
(mf/defc frame-and-shapes
|
||||
{:wrap [mf/wrap-memo]}
|
||||
[props]
|
||||
(let [data (mf/deref refs/workspace-data)
|
||||
shapes-map (:shapes-by-id data)
|
||||
shapes (->> (map #(get shapes-map %) (:shapes data []))
|
||||
(group-by :canvas))
|
||||
canvas (map #(get shapes-map %) (:canvas data []))]
|
||||
objects (:objects data)
|
||||
root (get objects uuid/zero)
|
||||
shapes (->> (:shapes root)
|
||||
(map #(get objects %)))]
|
||||
[:g.shapes
|
||||
(for [item canvas]
|
||||
[:& canvas-wrapper {:shape item
|
||||
:key (:id item)
|
||||
:childs (reverse (get shapes (:id item)))}])
|
||||
(for [item (reverse (get shapes nil))]
|
||||
[:& shape-wrapper {:shape item
|
||||
:key (:id item)}])]))
|
||||
(for [item shapes]
|
||||
(if (= (:type item) :frame)
|
||||
[:& frame-wrapper {:shape item
|
||||
:key (:id item)
|
||||
:objects objects}]
|
||||
[:& shape-wrapper {:shape item
|
||||
:key (:id item)}]))]))
|
||||
|
||||
|
||||
(mf/defc viewport
|
||||
[{:keys [page] :as props}]
|
||||
|
@ -169,139 +170,157 @@
|
|||
selected]
|
||||
:as local} (mf/deref refs/workspace-local)
|
||||
viewport-ref (mf/use-ref nil)
|
||||
zoom (or zoom 1)]
|
||||
(letfn [(on-mouse-down [event]
|
||||
(dom/stop-propagation event)
|
||||
(let [ctrl? (kbd/ctrl? event)
|
||||
shift? (kbd/shift? event)
|
||||
opts {:shift? shift?
|
||||
:ctrl? ctrl?}]
|
||||
(st/emit! (ms/->MouseEvent :down ctrl? shift?)))
|
||||
(when (not edition)
|
||||
(if drawing-tool
|
||||
(st/emit! (start-drawing drawing-tool))
|
||||
(st/emit! handle-selrect))))
|
||||
zoom (or zoom 1)
|
||||
|
||||
(on-context-menu [event]
|
||||
(dom/prevent-default event)
|
||||
(dom/stop-propagation event)
|
||||
(let [ctrl? (kbd/ctrl? event)
|
||||
shift? (kbd/shift? event)
|
||||
opts {:shift? shift?
|
||||
:ctrl? ctrl?}]
|
||||
(st/emit! (ms/->MouseEvent :context-menu ctrl? shift?))))
|
||||
on-mouse-down
|
||||
(fn [event]
|
||||
(dom/stop-propagation event)
|
||||
(let [ctrl? (kbd/ctrl? event)
|
||||
shift? (kbd/shift? event)
|
||||
opts {:shift? shift?
|
||||
:ctrl? ctrl?}]
|
||||
(st/emit! (ms/->MouseEvent :down ctrl? shift?)))
|
||||
(when (not edition)
|
||||
(if drawing-tool
|
||||
(st/emit! (start-drawing drawing-tool))
|
||||
(st/emit! handle-selrect))))
|
||||
|
||||
(on-mouse-up [event]
|
||||
(dom/stop-propagation event)
|
||||
(let [ctrl? (kbd/ctrl? event)
|
||||
shift? (kbd/shift? event)
|
||||
opts {:shift? shift?
|
||||
:ctrl? ctrl?}]
|
||||
(st/emit! (ms/->MouseEvent :up ctrl? shift?))))
|
||||
on-context-menu
|
||||
(fn [event]
|
||||
(dom/prevent-default event)
|
||||
(dom/stop-propagation event)
|
||||
(let [ctrl? (kbd/ctrl? event)
|
||||
shift? (kbd/shift? event)
|
||||
opts {:shift? shift?
|
||||
:ctrl? ctrl?}]
|
||||
(st/emit! (ms/->MouseEvent :context-menu ctrl? shift?))))
|
||||
|
||||
(on-click [event]
|
||||
(dom/stop-propagation event)
|
||||
(let [ctrl? (kbd/ctrl? event)
|
||||
shift? (kbd/shift? event)
|
||||
opts {:shift? shift?
|
||||
:ctrl? ctrl?}]
|
||||
(st/emit! (ms/->MouseEvent :click ctrl? shift?))))
|
||||
on-mouse-up
|
||||
(fn [event]
|
||||
(dom/stop-propagation event)
|
||||
(let [ctrl? (kbd/ctrl? event)
|
||||
shift? (kbd/shift? event)
|
||||
opts {:shift? shift?
|
||||
:ctrl? ctrl?}]
|
||||
(st/emit! (ms/->MouseEvent :up ctrl? shift?))))
|
||||
|
||||
(on-double-click [event]
|
||||
(dom/stop-propagation event)
|
||||
(let [ctrl? (kbd/ctrl? event)
|
||||
shift? (kbd/shift? event)
|
||||
opts {:shift? shift?
|
||||
:ctrl? ctrl?}]
|
||||
(st/emit! (ms/->MouseEvent :double-click ctrl? shift?))))
|
||||
on-click
|
||||
(fn [event]
|
||||
(dom/stop-propagation event)
|
||||
(let [ctrl? (kbd/ctrl? event)
|
||||
shift? (kbd/shift? event)
|
||||
opts {:shift? shift?
|
||||
:ctrl? ctrl?}]
|
||||
(st/emit! (ms/->MouseEvent :click ctrl? shift?))))
|
||||
|
||||
(translate-point-to-viewport [pt]
|
||||
(let [viewport (mf/ref-node viewport-ref)
|
||||
brect (.getBoundingClientRect viewport)
|
||||
brect (gpt/point (parse-int (.-left brect))
|
||||
(parse-int (.-top brect)))]
|
||||
(gpt/subtract pt brect)))
|
||||
on-double-click
|
||||
(fn [event]
|
||||
(dom/stop-propagation event)
|
||||
(let [ctrl? (kbd/ctrl? event)
|
||||
shift? (kbd/shift? event)
|
||||
opts {:shift? shift?
|
||||
:ctrl? ctrl?}]
|
||||
(st/emit! (ms/->MouseEvent :double-click ctrl? shift?))))
|
||||
|
||||
(on-key-down [event]
|
||||
(let [bevent (.getBrowserEvent event)
|
||||
key (.-keyCode event)
|
||||
ctrl? (kbd/ctrl? event)
|
||||
shift? (kbd/shift? event)
|
||||
opts {:key key
|
||||
:shift? shift?
|
||||
:ctrl? ctrl?}]
|
||||
(when-not (.-repeat bevent)
|
||||
(st/emit! (ms/->KeyboardEvent :down key ctrl? shift?))
|
||||
(when (kbd/space? event)
|
||||
(st/emit! handle-viewport-positioning)
|
||||
#_(st/emit! (dw/start-viewport-positioning))))))
|
||||
on-key-down
|
||||
(fn [event]
|
||||
(let [bevent (.getBrowserEvent event)
|
||||
key (.-keyCode event)
|
||||
ctrl? (kbd/ctrl? event)
|
||||
shift? (kbd/shift? event)
|
||||
opts {:key key
|
||||
:shift? shift?
|
||||
:ctrl? ctrl?}]
|
||||
(when-not (.-repeat bevent)
|
||||
(st/emit! (ms/->KeyboardEvent :down key ctrl? shift?))
|
||||
(when (kbd/space? event)
|
||||
(st/emit! handle-viewport-positioning)
|
||||
#_(st/emit! (dw/start-viewport-positioning))))))
|
||||
|
||||
(on-key-up [event]
|
||||
(let [key (.-keyCode event)
|
||||
ctrl? (kbd/ctrl? event)
|
||||
shift? (kbd/shift? event)
|
||||
opts {:key key
|
||||
:shift? shift?
|
||||
:ctrl? ctrl?}]
|
||||
(when (kbd/space? event)
|
||||
(st/emit! ::finish-positioning #_(dw/stop-viewport-positioning)))
|
||||
(st/emit! (ms/->KeyboardEvent :up key ctrl? shift?))))
|
||||
on-key-up
|
||||
(fn [event]
|
||||
(let [key (.-keyCode event)
|
||||
ctrl? (kbd/ctrl? event)
|
||||
shift? (kbd/shift? event)
|
||||
opts {:key key
|
||||
:shift? shift?
|
||||
:ctrl? ctrl?}]
|
||||
(when (kbd/space? event)
|
||||
(st/emit! ::finish-positioning #_(dw/stop-viewport-positioning)))
|
||||
(st/emit! (ms/->KeyboardEvent :up key ctrl? shift?))))
|
||||
|
||||
(on-mouse-move [event]
|
||||
(let [pt (gpt/point (.-clientX event)
|
||||
(.-clientY event))
|
||||
pt (translate-point-to-viewport pt)]
|
||||
(st/emit! (ms/->PointerEvent :viewport pt
|
||||
(kbd/ctrl? event)
|
||||
(kbd/shift? event)))))
|
||||
;; translate-point-to-viewport
|
||||
;; (fn [pt]
|
||||
;; (let [viewport (mf/ref-node viewport-ref)
|
||||
;; brect (.getBoundingClientRect viewport)
|
||||
;; brect (gpt/point (parse-int (.-left brect))
|
||||
;; (parse-int (.-top brect)))]
|
||||
;; (gpt/subtract pt brect)))
|
||||
|
||||
(on-mount []
|
||||
(let [key1 (events/listen js/document EventType.KEYDOWN on-key-down)
|
||||
key2 (events/listen js/document EventType.KEYUP on-key-up)]
|
||||
(fn []
|
||||
(events/unlistenByKey key1)
|
||||
(events/unlistenByKey key2))))]
|
||||
(mf/use-effect on-mount)
|
||||
[:*
|
||||
[:& coordinates {:zoom zoom}]
|
||||
[:svg.viewport {:width (* c/viewport-width zoom)
|
||||
:height (* c/viewport-height zoom)
|
||||
:ref viewport-ref
|
||||
:class (when drawing-tool "drawing")
|
||||
:on-context-menu on-context-menu
|
||||
:on-click on-click
|
||||
:on-double-click on-double-click
|
||||
:on-mouse-move on-mouse-move
|
||||
:on-mouse-down on-mouse-down
|
||||
:on-mouse-up on-mouse-up}
|
||||
[:g.zoom {:transform (str "scale(" zoom ", " zoom ")")}
|
||||
[:*
|
||||
[:& canvas-and-shapes]
|
||||
on-mouse-move
|
||||
(fn [event]
|
||||
;; NOTE: offsetX and offsetY are marked as "experimental" on
|
||||
;; MDN site but seems like they are supported on all
|
||||
;; browsers so we can avoid translation opetation just using
|
||||
;; this attributes.
|
||||
(let [;; pt (gpt/point (.-clientX event)
|
||||
;; (.-clientY event))
|
||||
;; pt (translate-point-to-viewport pt)
|
||||
pt (gpt/point (.-offsetX (.-nativeEvent event))
|
||||
(.-offsetY (.-nativeEvent event)))]
|
||||
(st/emit! (ms/->PointerEvent :viewport pt
|
||||
(kbd/ctrl? event)
|
||||
(kbd/shift? event)))))
|
||||
|
||||
(when (seq selected)
|
||||
[:& selection-handlers {:selected selected
|
||||
:zoom zoom
|
||||
:edition edition}])
|
||||
on-mount
|
||||
(fn []
|
||||
(let [key1 (events/listen js/document EventType.KEYDOWN on-key-down)
|
||||
key2 (events/listen js/document EventType.KEYUP on-key-up)]
|
||||
(fn []
|
||||
(events/unlistenByKey key1)
|
||||
(events/unlistenByKey key2))))]
|
||||
|
||||
(when-let [drawing-shape (:drawing local)]
|
||||
[:& draw-area {:shape drawing-shape
|
||||
:zoom zoom
|
||||
:modifiers (:modifiers local)}])]
|
||||
(mf/use-effect on-mount)
|
||||
[:*
|
||||
[:& coordinates {:zoom zoom}]
|
||||
[:svg.viewport {:width (* c/viewport-width zoom)
|
||||
:height (* c/viewport-height zoom)
|
||||
:ref viewport-ref
|
||||
:class (when drawing-tool "drawing")
|
||||
:on-context-menu on-context-menu
|
||||
:on-click on-click
|
||||
:on-double-click on-double-click
|
||||
:on-mouse-move on-mouse-move
|
||||
:on-mouse-down on-mouse-down
|
||||
:on-mouse-up on-mouse-up}
|
||||
[:g.zoom {:transform (str "scale(" zoom ", " zoom ")")}
|
||||
;; [:> js/React.Profiler
|
||||
;; {:id "foobar"
|
||||
;; :on-render (perf/react-on-profile)}
|
||||
;; [:& frame-and-shapes]]
|
||||
[:& frame-and-shapes]
|
||||
|
||||
(if (contains? flags :grid)
|
||||
[:& grid])]
|
||||
(when (seq selected)
|
||||
[:& selection-handlers {:selected selected
|
||||
:zoom zoom
|
||||
:edition edition}])
|
||||
|
||||
(when tooltip
|
||||
[:& cursor-tooltip {:zoom zoom :tooltip tooltip}])
|
||||
(when-let [drawing-shape (:drawing local)]
|
||||
[:& draw-area {:shape drawing-shape
|
||||
:zoom zoom
|
||||
:modifiers (:modifiers local)}])
|
||||
|
||||
(when (contains? flags :ruler)
|
||||
[:& ruler {:zoom zoom :ruler (:ruler local)}])
|
||||
(if (contains? flags :grid)
|
||||
[:& grid])]
|
||||
|
||||
(when tooltip
|
||||
[:& cursor-tooltip {:zoom zoom :tooltip tooltip}])
|
||||
|
||||
;; -- METER CURSOR MULTIUSUARIO
|
||||
[:& remote-user-cursors {:page page}]
|
||||
(when (contains? flags :ruler)
|
||||
[:& ruler {:zoom zoom :ruler (:ruler local)}])
|
||||
|
||||
[:& selrect {:data (:selrect local)}]]])))
|
||||
[:& remote-user-cursors {:page page}]
|
||||
[:& selrect {:data (:selrect local)}]]]))
|
||||
|
||||
|
||||
(mf/defc remote-user-cursor
|
||||
|
|
|
@ -11,3 +11,7 @@
|
|||
"Convert an es6 iterable into cljs Seq."
|
||||
[v]
|
||||
(seq (js/Array.from v)))
|
||||
|
||||
(defn obj-assign!
|
||||
[obj1 obj2]
|
||||
(js/Object.assign obj1 obj2))
|
||||
|
|
|
@ -6,7 +6,8 @@
|
|||
|
||||
(ns uxbox.util.perf
|
||||
"Performance and debugging tools."
|
||||
#?(:cljs (:require-macros [uxbox.util.perf])))
|
||||
#?(:cljs (:require-macros [uxbox.util.perf]))
|
||||
#?(:cljs (:require [uxbox.util.math :as math])))
|
||||
|
||||
#?(:clj
|
||||
(defmacro with-measure
|
||||
|
@ -17,3 +18,25 @@
|
|||
time# (.toFixed (- end# start#) 2)]
|
||||
(println (str "[perf|" ~name "] => " time#))
|
||||
res#)))
|
||||
|
||||
|
||||
;; id, // the "id" prop of the Profiler tree that has just committed
|
||||
;; phase, // either "mount" (if the tree just mounted) or "update" (if it re-rendered)
|
||||
;; actualDuration, // time spent rendering the committed update
|
||||
;; baseDuration, // estimated time to render the entire subtree without memoization
|
||||
;; startTime, // when React began rendering this update
|
||||
;; commitTime, // when React committed this update
|
||||
;; interactions // the Set of interactions belonging to this update
|
||||
|
||||
#?(:cljs
|
||||
(defn react-on-profile
|
||||
[]
|
||||
(let [sum (volatile! 0)
|
||||
ctr (volatile! 0)]
|
||||
(fn [id phase adur, bdur, st, ct, itx]
|
||||
(vswap! sum (fn [prev] (+ prev adur)))
|
||||
(vswap! ctr inc)
|
||||
(js/console.log (str "[profile:" id ":" phase "]")
|
||||
""
|
||||
(str "time=" (math/precision adur 4))
|
||||
(str "avg=" (math/precision (/ @sum @ctr) 4)))))))
|
||||
|
|
|
@ -13,7 +13,7 @@
|
|||
[uxbox.util.data :refer [seek]]
|
||||
[uxbox.view.data.viewer :as dv]
|
||||
[uxbox.view.store :as st]
|
||||
[uxbox.view.ui.viewer.canvas :refer [canvas]]
|
||||
[uxbox.view.ui.viewer.frame :refer [frame]]
|
||||
[uxbox.view.ui.viewer.nav :refer [nav]]
|
||||
[uxbox.view.ui.viewer.sitemap :refer [sitemap]]
|
||||
[lentes.core :as l]))
|
||||
|
@ -45,4 +45,4 @@
|
|||
:pages pages
|
||||
:selected id}])
|
||||
[:& nav {:flags flags}]
|
||||
[:& canvas {:page (seek #(= id (:id %)) pages)}]])))
|
||||
[:& frame {:page (seek #(= id (:id %)) pages)}]])))
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
;; Copyright (c) 2016-2017 Andrey Antukh <niwi@niwi.nz>
|
||||
;; Copyright (c) 2016-2017 Juan de la Cruz <delacruzgarciajuan@gmail.com>
|
||||
|
||||
(ns uxbox.view.ui.viewer.canvas
|
||||
(ns uxbox.view.ui.viewer.frame
|
||||
(:require
|
||||
[rumext.alpha :as mf]
|
||||
[uxbox.view.ui.viewer.shapes :as shapes]))
|
||||
|
@ -25,12 +25,12 @@
|
|||
|
||||
(declare shape)
|
||||
|
||||
(mf/defc canvas
|
||||
(mf/defc frame
|
||||
{:wrap [mf/wrap-memo]}
|
||||
[{:keys [page] :as props}]
|
||||
#_(let [{:keys [metadata id]} page
|
||||
{:keys [width height]} metadata]
|
||||
[:div.view-canvas
|
||||
[:div.view-frame
|
||||
[:svg.page-layout {:width width
|
||||
:height height}
|
||||
[:& background metadata]
|
Loading…
Reference in a new issue