♻️ 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:
Andrey Antukh 2020-03-08 12:46:09 +01:00
parent cbad98b783
commit ba373573e0
29 changed files with 1116 additions and 787 deletions

View file

@ -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

View file

@ -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)}])]]))

View file

@ -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?

View file

@ -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

View file

@ -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)

View file

@ -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))

View file

@ -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

View file

@ -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)

View file

@ -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]))

View file

@ -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

View file

@ -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

View file

@ -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")

View file

@ -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}])))

View file

@ -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]]]))

View file

@ -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}]))

View file

@ -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)

View file

@ -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

View file

@ -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))

View file

@ -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)))))))

View file

@ -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)}]])))

View file

@ -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]