mirror of
https://github.com/penpot/penpot.git
synced 2025-07-04 04:47:15 +02:00
🎉 Adds groups infrastructure
This commit is contained in:
parent
9d0450a4b5
commit
e73350e2ba
14 changed files with 423 additions and 209 deletions
|
@ -56,7 +56,7 @@
|
||||||
(s/def ::stroke-style #{:none :solid :dotted :dashed :mixed})
|
(s/def ::stroke-style #{:none :solid :dotted :dashed :mixed})
|
||||||
(s/def ::stroke-width number?)
|
(s/def ::stroke-width number?)
|
||||||
(s/def ::text-align #{"left" "right" "center" "justify"})
|
(s/def ::text-align #{"left" "right" "center" "justify"})
|
||||||
(s/def ::type #{:rect :path :circle :image :text :canvas :curve :icon :frame})
|
(s/def ::type #{:rect :path :circle :image :text :canvas :curve :icon :frame :group})
|
||||||
(s/def ::x number?)
|
(s/def ::x number?)
|
||||||
(s/def ::y number?)
|
(s/def ::y number?)
|
||||||
(s/def ::cx number?)
|
(s/def ::cx number?)
|
||||||
|
|
|
@ -13,7 +13,7 @@
|
||||||
funcool/lentes {:mvn/version "1.4.0-SNAPSHOT"}
|
funcool/lentes {:mvn/version "1.4.0-SNAPSHOT"}
|
||||||
funcool/potok {:mvn/version "2.8.0-SNAPSHOT"}
|
funcool/potok {:mvn/version "2.8.0-SNAPSHOT"}
|
||||||
funcool/promesa {:mvn/version "5.1.0"}
|
funcool/promesa {:mvn/version "5.1.0"}
|
||||||
funcool/rumext {:mvn/version "2020.03.24-1"
|
funcool/rumext {:mvn/version "2020.04.01-3"
|
||||||
:exclusions [cljsjs/react
|
:exclusions [cljsjs/react
|
||||||
cljsjs/react-dom]}
|
cljsjs/react-dom]}
|
||||||
}
|
}
|
||||||
|
|
20
frontend/src/uxbox/main/data/helpers.cljs
Normal file
20
frontend/src/uxbox/main/data/helpers.cljs
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
;; 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) 2020 UXBOX Labs SL
|
||||||
|
|
||||||
|
(ns uxbox.main.data.helpers)
|
||||||
|
|
||||||
|
(defn get-children
|
||||||
|
"Retrieve all children ids recursively for a given shape"
|
||||||
|
[shape-id objects]
|
||||||
|
(let [shapes (get-in objects [shape-id :shapes])]
|
||||||
|
(if shapes
|
||||||
|
(concat
|
||||||
|
shapes
|
||||||
|
(mapcat get-children shapes))
|
||||||
|
[])))
|
|
@ -23,6 +23,7 @@
|
||||||
[uxbox.main.constants :as c]
|
[uxbox.main.constants :as c]
|
||||||
[uxbox.main.data.icons :as udi]
|
[uxbox.main.data.icons :as udi]
|
||||||
[uxbox.main.data.dashboard :as dd]
|
[uxbox.main.data.dashboard :as dd]
|
||||||
|
[uxbox.main.data.helpers :as helpers]
|
||||||
[uxbox.main.geom :as geom]
|
[uxbox.main.geom :as geom]
|
||||||
[uxbox.main.refs :as refs]
|
[uxbox.main.refs :as refs]
|
||||||
[uxbox.main.repo :as rp]
|
[uxbox.main.repo :as rp]
|
||||||
|
@ -1512,13 +1513,32 @@
|
||||||
ptk/UpdateEvent
|
ptk/UpdateEvent
|
||||||
(update [_ state]
|
(update [_ state]
|
||||||
(let [page-id (::page-id state)
|
(let [page-id (::page-id state)
|
||||||
rfn (fn [state id]
|
objects (get-in state [:workspace-data page-id :objects])
|
||||||
(update-in state [:workspace-data page-id :objects id]
|
|
||||||
(fn [shape]
|
;; Updates the displacement data for a single shape
|
||||||
(let [mfr (:resize-modifier shape (gmt/matrix))]
|
materialize-shape
|
||||||
(-> (dissoc shape :resize-modifier)
|
(fn [state id mtx]
|
||||||
(geom/transform mfr))))))]
|
(update-in
|
||||||
(reduce rfn state ids)))
|
state
|
||||||
|
[:workspace-data page-id :objects id]
|
||||||
|
#(-> %
|
||||||
|
(dissoc :resize-modifier)
|
||||||
|
(geom/transform mtx))))
|
||||||
|
|
||||||
|
;; Applies materialize-shape over shape children
|
||||||
|
materialize-children
|
||||||
|
(fn [state id mtx]
|
||||||
|
(reduce #(materialize-shape %1 %2 mtx) state (helpers/get-children id objects)))
|
||||||
|
|
||||||
|
;; For each shape makes permanent the displacemnt
|
||||||
|
update-shapes
|
||||||
|
(fn [state id]
|
||||||
|
(let [shape (get objects id)
|
||||||
|
mtx (:resize-modifier shape (gmt/matrix))]
|
||||||
|
(-> state
|
||||||
|
(materialize-shape id mtx)
|
||||||
|
(materialize-children id mtx))))]
|
||||||
|
(reduce update-shapes state ids)))
|
||||||
|
|
||||||
ptk/WatchEvent
|
ptk/WatchEvent
|
||||||
(watch [_ state stream]
|
(watch [_ state stream]
|
||||||
|
@ -1552,13 +1572,33 @@
|
||||||
ptk/UpdateEvent
|
ptk/UpdateEvent
|
||||||
(update [_ state]
|
(update [_ state]
|
||||||
(let [page-id (::page-id state)
|
(let [page-id (::page-id state)
|
||||||
rfn (fn [state id]
|
objects (get-in state [:workspace-data page-id :objects])
|
||||||
(update-in state [:workspace-data page-id :objects id]
|
|
||||||
(fn [shape]
|
;; Updates the displacement data for a single shape
|
||||||
(let [mtx (:displacement-modifier shape (gmt/matrix))]
|
materialize-shape
|
||||||
(-> (dissoc shape :displacement-modifier)
|
(fn [state id mtx]
|
||||||
(geom/transform mtx))))))]
|
(update-in
|
||||||
(reduce rfn state ids)))
|
state
|
||||||
|
[:workspace-data page-id :objects id]
|
||||||
|
#(-> %
|
||||||
|
(dissoc :displacement-modifier)
|
||||||
|
(geom/transform mtx))))
|
||||||
|
|
||||||
|
;; Applies materialize-shape over shape children
|
||||||
|
materialize-children
|
||||||
|
(fn [state id mtx]
|
||||||
|
(reduce #(materialize-shape %1 %2 mtx) state (helpers/get-children id objects)))
|
||||||
|
|
||||||
|
;; For each shape makes permanent the resize
|
||||||
|
update-shapes
|
||||||
|
(fn [state id]
|
||||||
|
(let [shape (get objects id)
|
||||||
|
mtx (:displacement-modifier shape (gmt/matrix))]
|
||||||
|
(-> state
|
||||||
|
(materialize-shape id mtx)
|
||||||
|
(materialize-children id mtx))))]
|
||||||
|
|
||||||
|
(reduce update-shapes state ids)))
|
||||||
|
|
||||||
ptk/WatchEvent
|
ptk/WatchEvent
|
||||||
(watch [_ state stream]
|
(watch [_ state stream]
|
||||||
|
@ -2123,6 +2163,73 @@
|
||||||
(assoc-in state [:projects (:project-id page) :pages] pages)))))
|
(assoc-in state [:projects (:project-id page) :pages] pages)))))
|
||||||
|
|
||||||
|
|
||||||
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||||
|
;; GROUPS
|
||||||
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||||
|
|
||||||
|
(defn get-parent [object-id objects]
|
||||||
|
(let [include-object
|
||||||
|
(fn [object]
|
||||||
|
(and (:shapes object)
|
||||||
|
(some #(= object-id %) (:shapes object))))]
|
||||||
|
(first (filter include-object objects))))
|
||||||
|
|
||||||
|
(defn group-shape [id frame-id selected selection-rect]
|
||||||
|
{:id id
|
||||||
|
:type :group
|
||||||
|
:name (name (gensym "Group-"))
|
||||||
|
:shapes (vec selected)
|
||||||
|
:frame-id frame-id
|
||||||
|
:x (:x selection-rect)
|
||||||
|
:y (:y selection-rect)
|
||||||
|
:width (:width selection-rect)
|
||||||
|
:height (:height selection-rect)})
|
||||||
|
|
||||||
|
(defn create-group []
|
||||||
|
(let [id (uuid/next)]
|
||||||
|
(ptk/reify ::create-group
|
||||||
|
ptk/UpdateEvent
|
||||||
|
(update [_ state]
|
||||||
|
(let [selected (get-in state [:workspace-local :selected])]
|
||||||
|
(if (and selected (-> selected count (> 1)))
|
||||||
|
(let [page-id (get-in state [:workspace-page :id])
|
||||||
|
objects (get-in state [:workspace-data page-id :objects])
|
||||||
|
parent (get-parent (first selected) (vals objects))
|
||||||
|
selected-objects (map (partial get objects) selected)
|
||||||
|
selection-rect (geom/selection-rect selected-objects)
|
||||||
|
new-shape (group-shape id (-> selected-objects first :frame-id) selected selection-rect)
|
||||||
|
objects-removed (-> objects
|
||||||
|
#_(apply dissoc $ selected)
|
||||||
|
(assoc (:id new-shape) new-shape)
|
||||||
|
(update-in [(:id parent) :shapes]
|
||||||
|
(fn [shapes] (filter #(not (selected %)) shapes)))
|
||||||
|
(update-in [(:id parent) :shapes] conj (:id new-shape)))]
|
||||||
|
(-> state
|
||||||
|
(assoc-in [:workspace-data page-id :objects] objects-removed )
|
||||||
|
(assoc-in [:workspace-local :selected] #{(:id new-shape)})))
|
||||||
|
state)))
|
||||||
|
|
||||||
|
ptk/WatchEvent
|
||||||
|
(watch [_ state stream]
|
||||||
|
(let [obj (get-in state [:workspace-data (::page-id state) :objects id])
|
||||||
|
frame-id (:frame-id obj)
|
||||||
|
frame (get-in state [:workspace-data (::page-id state) :objects frame-id])]
|
||||||
|
(rx/of (commit-changes [{:type :add-obj
|
||||||
|
:id id
|
||||||
|
:frame-id (:frame-id obj)
|
||||||
|
:obj obj}
|
||||||
|
{:type :mod-obj
|
||||||
|
:id frame-id
|
||||||
|
:operations [{:type :set
|
||||||
|
:attr :shapes
|
||||||
|
:val (:shapes frame)}]}]
|
||||||
|
[{:type :del-obj :id id}
|
||||||
|
{:type :mod-obj
|
||||||
|
:id frame-id
|
||||||
|
:operations [{:type :set
|
||||||
|
:attr :shapes
|
||||||
|
:val (into (:shapes frame) (:shapes obj))}]}])))))))
|
||||||
|
|
||||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||||
;; Shortcuts
|
;; Shortcuts
|
||||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||||
|
@ -2143,6 +2250,8 @@
|
||||||
"ctrl+t" #(rx/of (select-for-drawing :text))
|
"ctrl+t" #(rx/of (select-for-drawing :text))
|
||||||
"ctrl+c" #(rx/of copy-selected)
|
"ctrl+c" #(rx/of copy-selected)
|
||||||
"ctrl+v" #(rx/of paste)
|
"ctrl+v" #(rx/of paste)
|
||||||
|
"ctrl+g" #(rx/of (create-group))
|
||||||
|
;; "ctrl+shift+g" #(rx/of remove-group)
|
||||||
"esc" #(rx/of :interrupt deselect-all)
|
"esc" #(rx/of :interrupt deselect-all)
|
||||||
"delete" #(rx/of delete-selected)
|
"delete" #(rx/of delete-selected)
|
||||||
"ctrl+up" #(rx/of (vertical-order-selected :up))
|
"ctrl+up" #(rx/of (vertical-order-selected :up))
|
||||||
|
|
|
@ -18,7 +18,8 @@
|
||||||
[uxbox.main.ui.shapes.image :as image]
|
[uxbox.main.ui.shapes.image :as image]
|
||||||
[uxbox.main.ui.shapes.path :as path]
|
[uxbox.main.ui.shapes.path :as path]
|
||||||
[uxbox.main.ui.shapes.rect :as rect]
|
[uxbox.main.ui.shapes.rect :as rect]
|
||||||
[uxbox.main.ui.shapes.text :as text]))
|
[uxbox.main.ui.shapes.text :as text]
|
||||||
|
[uxbox.main.ui.shapes.group :as group]))
|
||||||
|
|
||||||
(mf/defc background
|
(mf/defc background
|
||||||
[]
|
[]
|
||||||
|
@ -37,13 +38,21 @@
|
||||||
{:width (if (mth/nan? width) 100 width)
|
{:width (if (mth/nan? width) 100 width)
|
||||||
:height (if (mth/nan? height) 100 height)}))
|
:height (if (mth/nan? height) 100 height)}))
|
||||||
|
|
||||||
|
(declare frame-shape)
|
||||||
|
(declare group-shape)
|
||||||
|
|
||||||
(mf/defc frame-wrapper
|
(mf/defc frame-wrapper
|
||||||
[{:keys [shape objects] :as props}]
|
[{:keys [shape objects] :as props}]
|
||||||
(let [childs (mapv #(get objects %) (:shapes shape))]
|
(let [childs (mapv #(get objects %) (:shapes shape))]
|
||||||
[:& frame/frame-shape {:shape shape :childs childs}]))
|
[:& frame-shape {:shape shape :childs childs}]))
|
||||||
|
|
||||||
|
(mf/defc group-wrapper
|
||||||
|
[{:keys [shape-wrapper shape objects] :as props}]
|
||||||
|
(let [children (mapv #(get objects %) (:shapes shape))]
|
||||||
|
[:& group-shape {:shape shape :children children}]))
|
||||||
|
|
||||||
(mf/defc shape-wrapper
|
(mf/defc shape-wrapper
|
||||||
[{:keys [shape] :as props}]
|
[{:keys [shape objects] :as props}]
|
||||||
(when (and shape (not (:hidden shape)))
|
(when (and shape (not (:hidden shape)))
|
||||||
(case (:type shape)
|
(case (:type shape)
|
||||||
:frame [:& rect/rect-shape {:shape shape}]
|
:frame [:& rect/rect-shape {:shape shape}]
|
||||||
|
@ -53,7 +62,12 @@
|
||||||
:rect [:& rect/rect-shape {:shape shape}]
|
:rect [:& rect/rect-shape {:shape shape}]
|
||||||
:path [:& path/path-shape {:shape shape}]
|
:path [:& path/path-shape {:shape shape}]
|
||||||
:image [:& image/image-shape {:shape shape}]
|
:image [:& image/image-shape {:shape shape}]
|
||||||
:circle [:& circle/circle-shape {:shape shape}])))
|
:circle [:& circle/circle-shape {:shape shape}]
|
||||||
|
:group [:& (group/group-shape shape-wrapper) {:shape shape :shape-wrapper shape-wrapper :objects objects}]
|
||||||
|
nil)))
|
||||||
|
|
||||||
|
(def group-shape (group/group-shape shape-wrapper))
|
||||||
|
(def frame-shape (frame/frame-shape shape-wrapper))
|
||||||
|
|
||||||
(mf/defc page-svg
|
(mf/defc page-svg
|
||||||
[{:keys [data] :as props}]
|
[{:keys [data] :as props}]
|
||||||
|
@ -73,7 +87,8 @@
|
||||||
:key (:id item)
|
:key (:id item)
|
||||||
:objects objects}]
|
:objects objects}]
|
||||||
[:& shape-wrapper {:shape item
|
[:& shape-wrapper {:shape item
|
||||||
:key (:id item)}]))]))
|
:key (:id item)
|
||||||
|
:objects objects}]))]))
|
||||||
|
|
||||||
;; (defn- render-html
|
;; (defn- render-html
|
||||||
;; [component]
|
;; [component]
|
||||||
|
|
|
@ -32,7 +32,8 @@
|
||||||
:curve (move-path shape dpoint)
|
:curve (move-path shape dpoint)
|
||||||
:path (move-path shape dpoint)
|
:path (move-path shape dpoint)
|
||||||
:circle (move-circle shape dpoint)
|
:circle (move-circle shape dpoint)
|
||||||
nil))
|
:group (move-rect shape dpoint)
|
||||||
|
shape))
|
||||||
|
|
||||||
(defn- move-rect
|
(defn- move-rect
|
||||||
"A specialized function for relative movement
|
"A specialized function for relative movement
|
||||||
|
@ -73,7 +74,9 @@
|
||||||
:frame (absolute-move-rect shape position)
|
:frame (absolute-move-rect shape position)
|
||||||
:image (absolute-move-rect shape position)
|
:image (absolute-move-rect shape position)
|
||||||
:rect (absolute-move-rect shape position)
|
:rect (absolute-move-rect shape position)
|
||||||
:circle (absolute-move-circle shape position)))
|
:group (absolute-move-rect shape position)
|
||||||
|
:circle (absolute-move-circle shape position)
|
||||||
|
shape))
|
||||||
|
|
||||||
(defn- absolute-move-rect
|
(defn- absolute-move-rect
|
||||||
"A specialized function for absolute moviment
|
"A specialized function for absolute moviment
|
||||||
|
@ -493,6 +496,7 @@
|
||||||
[objects shape]
|
[objects shape]
|
||||||
(case (:type shape)
|
(case (:type shape)
|
||||||
:rect (resolve-rect-shape objects shape)
|
:rect (resolve-rect-shape objects shape)
|
||||||
|
:group (resolve-rect-shape objects shape)
|
||||||
:frame (resolve-rect-shape objects shape)))
|
:frame (resolve-rect-shape objects shape)))
|
||||||
|
|
||||||
(defn- resolve-rect-shape
|
(defn- resolve-rect-shape
|
||||||
|
@ -511,15 +515,19 @@
|
||||||
(defn transform
|
(defn transform
|
||||||
"Apply the matrix transformation to shape."
|
"Apply the matrix transformation to shape."
|
||||||
[{:keys [type] :as shape} xfmt]
|
[{:keys [type] :as shape} xfmt]
|
||||||
(case type
|
(if (gmt/matrix? xfmt)
|
||||||
:frame (transform-rect shape xfmt)
|
(case type
|
||||||
:rect (transform-rect shape xfmt)
|
:frame (transform-rect shape xfmt)
|
||||||
:icon (transform-rect shape xfmt)
|
:group (transform-rect shape xfmt)
|
||||||
:text (transform-rect shape xfmt)
|
:rect (transform-rect shape xfmt)
|
||||||
:image (transform-rect shape xfmt)
|
:icon (transform-rect shape xfmt)
|
||||||
:path (transform-path shape xfmt)
|
:text (transform-rect shape xfmt)
|
||||||
:curve (transform-path shape xfmt)
|
:image (transform-rect shape xfmt)
|
||||||
:circle (transform-circle shape xfmt)))
|
:path (transform-path shape xfmt)
|
||||||
|
:curve (transform-path shape xfmt)
|
||||||
|
:circle (transform-circle shape xfmt)
|
||||||
|
shape)
|
||||||
|
shape))
|
||||||
|
|
||||||
(defn- transform-rect
|
(defn- transform-rect
|
||||||
[{:keys [x y width height] :as shape} mx]
|
[{:keys [x y width height] :as shape} mx]
|
||||||
|
|
|
@ -47,9 +47,13 @@
|
||||||
(l/derive st/state)))
|
(l/derive st/state)))
|
||||||
|
|
||||||
(def workspace-data
|
(def workspace-data
|
||||||
(-> (l/lens (fn [state]
|
(-> (l/lens #(let [page-id (get-in % [:workspace-page :id])]
|
||||||
(let [page-id (get-in state [:workspace-page :id])]
|
(get-in % [:workspace-data page-id])))
|
||||||
(get-in state [:workspace-data page-id]))))
|
(l/derive st/state)))
|
||||||
|
|
||||||
|
(def objects
|
||||||
|
(-> (l/lens #(let [page-id (get-in % [:workspace-page :id])]
|
||||||
|
(get-in % [:workspace-data page-id :objects])))
|
||||||
(l/derive st/state)))
|
(l/derive st/state)))
|
||||||
|
|
||||||
(def selected-shapes
|
(def selected-shapes
|
||||||
|
|
|
@ -42,7 +42,6 @@
|
||||||
(rx/filter (fn [s] (deref *debug*)) $)
|
(rx/filter (fn [s] (deref *debug*)) $)
|
||||||
(rx/subscribe $ (fn [event]
|
(rx/subscribe $ (fn [event]
|
||||||
(println "[stream]: " (repr-event event)))))))
|
(println "[stream]: " (repr-event event)))))))
|
||||||
|
|
||||||
(def auth-ref
|
(def auth-ref
|
||||||
(-> (l/key :auth)
|
(-> (l/key :auth)
|
||||||
(l/derive state)))
|
(l/derive state)))
|
||||||
|
|
|
@ -12,7 +12,8 @@
|
||||||
(:require
|
(:require
|
||||||
[beicon.core :as rx]
|
[beicon.core :as rx]
|
||||||
[goog.object :as gobj]
|
[goog.object :as gobj]
|
||||||
[rumext.alpha :as mf]))
|
[rumext.alpha :as mf]
|
||||||
|
[cljsjs.react]))
|
||||||
|
|
||||||
(defn wrap-catch
|
(defn wrap-catch
|
||||||
[component {:keys [fallback on-error]}]
|
[component {:keys [fallback on-error]}]
|
||||||
|
|
|
@ -13,7 +13,8 @@
|
||||||
[rumext.alpha :as mf]
|
[rumext.alpha :as mf]
|
||||||
[uxbox.main.refs :as refs]
|
[uxbox.main.refs :as refs]
|
||||||
[uxbox.main.store :as st]
|
[uxbox.main.store :as st]
|
||||||
[uxbox.main.ui.shapes.frame :as frame]))
|
[uxbox.main.ui.shapes.frame :as frame]
|
||||||
|
[uxbox.main.ui.shapes.shape :as shape]))
|
||||||
|
|
||||||
(def shape-wrapper frame/shape-wrapper)
|
(def shape-wrapper shape/shape-wrapper)
|
||||||
(def frame-wrapper frame/frame-wrapper)
|
(def frame-wrapper (frame/frame-wrapper shape-wrapper))
|
||||||
|
|
|
@ -18,13 +18,7 @@
|
||||||
[uxbox.main.refs :as refs]
|
[uxbox.main.refs :as refs]
|
||||||
[uxbox.main.store :as st]
|
[uxbox.main.store :as st]
|
||||||
[uxbox.main.ui.shapes.attrs :as attrs]
|
[uxbox.main.ui.shapes.attrs :as attrs]
|
||||||
[uxbox.main.ui.shapes.circle :as circle]
|
|
||||||
[uxbox.main.ui.shapes.common :as common]
|
[uxbox.main.ui.shapes.common :as common]
|
||||||
[uxbox.main.ui.shapes.icon :as icon]
|
|
||||||
[uxbox.main.ui.shapes.image :as image]
|
|
||||||
[uxbox.main.ui.shapes.path :as path]
|
|
||||||
[uxbox.main.ui.shapes.rect :as rect]
|
|
||||||
[uxbox.main.ui.shapes.text :as text]
|
|
||||||
[uxbox.util.dom :as dom]
|
[uxbox.util.dom :as dom]
|
||||||
[uxbox.util.interop :as itr]
|
[uxbox.util.interop :as itr]
|
||||||
[uxbox.util.geom.matrix :as gmt]
|
[uxbox.util.geom.matrix :as gmt]
|
||||||
|
@ -32,39 +26,14 @@
|
||||||
|
|
||||||
(declare frame-wrapper)
|
(declare frame-wrapper)
|
||||||
|
|
||||||
|
(def frame-default-props {:fill-color "#ffffff"})
|
||||||
(defn wrap-memo-shape
|
|
||||||
([component]
|
|
||||||
(js/React.memo
|
|
||||||
component
|
|
||||||
(fn [np op]
|
|
||||||
(let [n-shape (unchecked-get np "shape")
|
|
||||||
o-shape (unchecked-get op "shape")]
|
|
||||||
(= n-shape o-shape))))))
|
|
||||||
|
|
||||||
(mf/defc shape-wrapper
|
|
||||||
{::mf/wrap [wrap-memo-shape]}
|
|
||||||
[{:keys [shape] :as props}]
|
|
||||||
(when (and shape (not (:hidden shape)))
|
|
||||||
(case (:type shape)
|
|
||||||
:frame [:& frame-wrapper {:shape shape :childs []}]
|
|
||||||
:curve [:& path/path-wrapper {:shape shape}]
|
|
||||||
:text [:& text/text-wrapper {:shape shape}]
|
|
||||||
:icon [:& icon/icon-wrapper {:shape shape}]
|
|
||||||
:rect [:& rect/rect-wrapper {:shape shape}]
|
|
||||||
:path [:& path/path-wrapper {:shape shape}]
|
|
||||||
:image [:& image/image-wrapper {:shape shape}]
|
|
||||||
:circle [:& circle/circle-wrapper {:shape shape}])))
|
|
||||||
|
|
||||||
(def frame-default-props
|
|
||||||
{:fill-color "#ffffff"})
|
|
||||||
|
|
||||||
(declare frame-shape)
|
(declare frame-shape)
|
||||||
(declare translate-to-frame)
|
(declare translate-to-frame)
|
||||||
|
|
||||||
(defn wrap-memo-frame
|
(defn wrap-memo-frame
|
||||||
([component]
|
([component]
|
||||||
(js/React.memo
|
(mf/memo'
|
||||||
component
|
component
|
||||||
(fn [np op]
|
(fn [np op]
|
||||||
(let [n-shape (aget np "shape")
|
(let [n-shape (aget np "shape")
|
||||||
|
@ -84,76 +53,83 @@
|
||||||
false)))))))))
|
false)))))))))
|
||||||
|
|
||||||
|
|
||||||
(mf/defc frame-wrapper
|
(defn frame-wrapper [shape-wrapper]
|
||||||
{::mf/wrap [wrap-memo-frame]}
|
(mf/fnc frame-wrapper
|
||||||
[{:keys [shape objects] :as props}]
|
{::mf/wrap [wrap-memo-frame]}
|
||||||
(when (and shape (not (:hidden shape)))
|
[{:keys [shape objects] :as props}]
|
||||||
(let [selected-iref (-> (mf/deps (:id shape))
|
(when (and shape (not (:hidden shape)))
|
||||||
(mf/use-memo #(refs/make-selected (:id shape))))
|
(let [selected-iref (-> (mf/deps (:id shape))
|
||||||
selected? (mf/deref selected-iref)
|
(mf/use-memo #(refs/make-selected (:id shape))))
|
||||||
on-mouse-down #(common/on-mouse-down % shape)
|
selected? (mf/deref selected-iref)
|
||||||
on-context-menu #(common/on-context-menu % shape)
|
on-mouse-down #(common/on-mouse-down % shape)
|
||||||
shape (merge frame-default-props shape)
|
on-context-menu #(common/on-context-menu % shape)
|
||||||
{:keys [x y width height]} shape
|
shape (merge frame-default-props shape)
|
||||||
|
{:keys [x y width height]} shape
|
||||||
|
|
||||||
childs (mapv #(get objects %) (:shapes shape))
|
childs (mapv #(get objects %) (:shapes shape))
|
||||||
|
|
||||||
ds-modifier (:displacement-modifier shape)
|
ds-modifier (:displacement-modifier shape)
|
||||||
label-pos (cond-> (gpt/point x (- y 10))
|
label-pos (cond-> (gpt/point x (- y 10))
|
||||||
(gmt/matrix? ds-modifier) (gpt/transform ds-modifier))
|
(gmt/matrix? ds-modifier) (gpt/transform ds-modifier))
|
||||||
|
|
||||||
on-double-click
|
on-double-click
|
||||||
(fn [event]
|
(fn [event]
|
||||||
(dom/prevent-default event)
|
(dom/prevent-default event)
|
||||||
(st/emit! dw/deselect-all
|
(st/emit! dw/deselect-all
|
||||||
(dw/select-shape (:id shape))))]
|
(dw/select-shape (:id shape))))]
|
||||||
[:g {:class (when selected? "selected")
|
[:g {:class (when selected? "selected")
|
||||||
:on-context-menu on-context-menu
|
:on-context-menu on-context-menu
|
||||||
:on-double-click on-double-click
|
:on-double-click on-double-click
|
||||||
:on-mouse-down on-mouse-down}
|
:on-mouse-down on-mouse-down}
|
||||||
[:text {:x (:x label-pos)
|
[:text {:x (:x label-pos)
|
||||||
:y (:y label-pos)
|
:y (:y label-pos)
|
||||||
:width width
|
:width width
|
||||||
:height 20
|
:height 20
|
||||||
:class-name "workspace-frame-label"
|
:class-name "workspace-frame-label"
|
||||||
:on-click on-double-click} ; user may also select with single click in the label
|
:on-click on-double-click} ; user may also select with single click in the label
|
||||||
(:name shape)]
|
(:name shape)]
|
||||||
[:& frame-shape {:shape shape :childs childs}]])))
|
[:& (frame-shape shape-wrapper) {:shape shape
|
||||||
|
:childs childs}]]))))
|
||||||
|
|
||||||
(mf/defc frame-shape
|
(defn frame-shape [shape-wrapper]
|
||||||
[{:keys [shape childs] :as props}]
|
(mf/fnc frame-shape
|
||||||
(let [rotation (:rotation shape)
|
[{:keys [shape childs] :as props}]
|
||||||
ds-modifier (:displacement-modifier shape)
|
(let [rotation (:rotation shape)
|
||||||
rz-modifier (:resize-modifier shape)
|
ds-modifier (:displacement-modifier shape)
|
||||||
|
rz-modifier (:resize-modifier shape)
|
||||||
|
|
||||||
shape (cond-> shape
|
shape (cond-> shape
|
||||||
(gmt/matrix? rz-modifier) (geom/transform rz-modifier)
|
(gmt/matrix? rz-modifier) (geom/transform rz-modifier)
|
||||||
(gmt/matrix? ds-modifier) (geom/transform ds-modifier))
|
(gmt/matrix? ds-modifier) (geom/transform ds-modifier))
|
||||||
|
|
||||||
{:keys [id x y width height]} shape
|
{:keys [id x y width height]} shape
|
||||||
|
|
||||||
props (-> (attrs/extract-style-attrs shape)
|
props (-> (attrs/extract-style-attrs shape)
|
||||||
(itr/obj-assign!
|
(itr/obj-assign!
|
||||||
#js {:x 0
|
#js {:x 0
|
||||||
:y 0
|
:y 0
|
||||||
:id (str "shape-" id)
|
:id (str "shape-" id)
|
||||||
:width width
|
:width width
|
||||||
:height height}))
|
:height height}))]
|
||||||
|
|
||||||
translate #(translate-to-frame % ds-modifier (gpt/point (- x) (- y)))]
|
[:svg {:x x :y y :width width :height height}
|
||||||
[:svg {:x x :y y :width width :height height}
|
[:> "rect" props]
|
||||||
[:> "rect" props]
|
(for [item childs]
|
||||||
(for [item childs]
|
[:& shape-wrapper {:shape (translate-to-frame item shape) :key (:id item)}])])))
|
||||||
[:& shape-wrapper {:shape (translate item) :key (:id item)}])]))
|
|
||||||
|
|
||||||
(defn- translate-to-frame
|
(defn- translate-to-frame
|
||||||
[shape frame-ds-modifier pt]
|
[shape frame]
|
||||||
(let [rz-modifier (:resize-modifier shape)
|
(let [pt (gpt/point (- (:x frame)) (- (:y frame)))
|
||||||
|
frame-ds-modifier (:displacement-modifier frame)
|
||||||
|
rz-modifier (:resize-modifier shape)
|
||||||
shape (cond-> shape
|
shape (cond-> shape
|
||||||
(gmt/matrix? frame-ds-modifier)
|
(gmt/matrix? frame-ds-modifier)
|
||||||
(geom/transform frame-ds-modifier)
|
(geom/transform frame-ds-modifier)
|
||||||
|
|
||||||
(gmt/matrix? rz-modifier)
|
(and (= (:type shape) :group) (gmt/matrix? rz-modifier))
|
||||||
|
(geom/transform rz-modifier)
|
||||||
|
|
||||||
|
(and (not= (:type shape) :group) (gmt/matrix? rz-modifier))
|
||||||
(-> (geom/transform rz-modifier)
|
(-> (geom/transform rz-modifier)
|
||||||
(dissoc :resize-modifier)))]
|
(dissoc :resize-modifier)))]
|
||||||
(geom/move shape pt)))
|
(geom/move shape pt)))
|
||||||
|
|
|
@ -5,82 +5,91 @@
|
||||||
;; Copyright (c) 2016-2019 Andrey Antukh <niwi@niwi.nz>
|
;; Copyright (c) 2016-2019 Andrey Antukh <niwi@niwi.nz>
|
||||||
|
|
||||||
(ns uxbox.main.ui.shapes.group
|
(ns uxbox.main.ui.shapes.group
|
||||||
#_(:require
|
(:require
|
||||||
[lentes.core :as l]
|
[cuerdas.core :as str]
|
||||||
|
[rumext.alpha :as mf]
|
||||||
[uxbox.main.geom :as geom]
|
[uxbox.main.geom :as geom]
|
||||||
[uxbox.main.refs :as refs]
|
[uxbox.main.refs :as refs]
|
||||||
[uxbox.main.store :as st]
|
[uxbox.util.dom :as dom]
|
||||||
[uxbox.main.ui.shapes.attrs :as attrs]
|
[uxbox.util.geom.matrix :as gmt]
|
||||||
[uxbox.main.ui.shapes.circle :as circle]
|
[uxbox.util.geom.point :as gpt]
|
||||||
|
[uxbox.util.interop :as itr]
|
||||||
[uxbox.main.ui.shapes.common :as common]
|
[uxbox.main.ui.shapes.common :as common]
|
||||||
[uxbox.main.ui.shapes.icon :as icon]
|
[uxbox.main.ui.shapes.attrs :as attrs]))
|
||||||
[uxbox.main.ui.shapes.image :as image]
|
|
||||||
[uxbox.main.ui.shapes.path :as path]
|
|
||||||
[uxbox.main.ui.shapes.rect :as rect]
|
|
||||||
[uxbox.main.ui.shapes.text :as text]
|
|
||||||
[uxbox.util.data :refer [classnames]]
|
|
||||||
[uxbox.util.geom.matrix :as gmt]))
|
|
||||||
|
|
||||||
;; --- Helpers
|
(declare translate-to-frame)
|
||||||
|
(declare group-shape)
|
||||||
|
|
||||||
;; (declare group-component)
|
(defn group-wrapper [shape-wrapper]
|
||||||
|
(mf/fnc group-wrapper
|
||||||
|
{::mf/wrap-props false}
|
||||||
|
[props]
|
||||||
|
(let [shape (unchecked-get props "shape")
|
||||||
|
on-mouse-down #(common/on-mouse-down % shape)
|
||||||
|
on-context-menu #(common/on-context-menu % shape)
|
||||||
|
objects (-> refs/objects mf/deref)
|
||||||
|
children (mapv #(get objects %) (:shapes shape))
|
||||||
|
frame (get objects (:frame-id shape))]
|
||||||
|
[:g.shape {:on-mouse-down on-mouse-down
|
||||||
|
:on-context-menu on-context-menu}
|
||||||
|
[:& (group-shape shape-wrapper) {:shape shape
|
||||||
|
:shape-wrapper shape-wrapper
|
||||||
|
:children children
|
||||||
|
:frame frame }]])))
|
||||||
|
|
||||||
;; (defn- focus-shape
|
(defn group-shape [shape-wrapper]
|
||||||
;; [id]
|
(mf/fnc group-shape
|
||||||
;; (-> (l/in [:shapes id])
|
{::mf/wrap-props false}
|
||||||
;; (l/derive st/state)))
|
[props]
|
||||||
|
(let [shape (unchecked-get props "shape")
|
||||||
|
children (unchecked-get props "children")
|
||||||
|
frame (unchecked-get props "frame")
|
||||||
|
|
||||||
;; (defn render-component
|
ds-modifier (:displacement-modifier shape)
|
||||||
;; [shape]
|
rz-modifier (:resize-modifier shape)
|
||||||
;; (case (:type shape)
|
|
||||||
;; :group (group-component shape)
|
|
||||||
;; :text (text/text-component shape)
|
|
||||||
;; :icon (icon/icon-component shape)
|
|
||||||
;; :rect (rect/rect-component shape)
|
|
||||||
;; :path (path/path-component shape)
|
|
||||||
;; :image (image/image-component shape)
|
|
||||||
;; :circle (circle/circle-component shape)))
|
|
||||||
|
|
||||||
;; (mx/defc component-container
|
shape (cond-> shape
|
||||||
;; {:mixins [mx/reactive mx/static]}
|
(and (= "root" (:name frame)) (gmt/matrix? rz-modifier)) (geom/transform rz-modifier)
|
||||||
;; [id]
|
(gmt/matrix? rz-modifier) (geom/transform ds-modifier))
|
||||||
;; (when-let [shape (mx/react (focus-shape id))]
|
|
||||||
;; (when-not (:hidden shape)
|
|
||||||
;; (render-component shape))))
|
|
||||||
|
|
||||||
;; ;; --- Group Component
|
{:keys [id x y width height rotation]} shape
|
||||||
|
|
||||||
;; (declare group-shape)
|
transform (when (and rotation (pos? rotation))
|
||||||
|
(str/format "rotate(%s %s %s)"
|
||||||
|
rotation
|
||||||
|
(+ x (/ width 2))
|
||||||
|
(+ y (/ height 2))))]
|
||||||
|
[:g
|
||||||
|
(for [item (reverse children)]
|
||||||
|
[:& shape-wrapper {:shape (-> item
|
||||||
|
(geom/transform rz-modifier)
|
||||||
|
(assoc :displacement-modifier ds-modifier)
|
||||||
|
(translate-to-frame frame))
|
||||||
|
:key (:id item)}])
|
||||||
|
|
||||||
|
[:rect {:x x
|
||||||
|
:y y
|
||||||
|
:fill "red"
|
||||||
|
:opacity 0.8
|
||||||
|
:transform transform
|
||||||
|
:id (str "group-" id)
|
||||||
|
:width width
|
||||||
|
:height height}]])))
|
||||||
|
|
||||||
;; (mx/defc group-component
|
(defn- translate-to-frame
|
||||||
;; {:mixins [mx/static mx/reactive]}
|
[shape frame]
|
||||||
;; [{:keys [id x y width height group] :as shape}]
|
(let [pt (gpt/point (- (:x frame)) (- (:y frame)))
|
||||||
;; (let [modifiers (mx/react (refs/selected-modifiers id))
|
frame-ds-modifier (:displacement-modifier frame)
|
||||||
;; selected (mx/react refs/selected-shapes)
|
rz-modifier (:resize-modifier shape)
|
||||||
;; selected? (contains? selected id)
|
shape (cond-> shape
|
||||||
;; on-mouse-down #(common/on-mouse-down % shape selected)
|
(gmt/matrix? frame-ds-modifier)
|
||||||
;; shape (assoc shape :modifiers modifiers)]
|
(geom/transform frame-ds-modifier)
|
||||||
;; [:g.shape.group-shape
|
|
||||||
;; {:class (when selected? "selected")
|
|
||||||
;; :on-mouse-down on-mouse-down}
|
|
||||||
;; (group-shape shape component-container)]))
|
|
||||||
|
|
||||||
;; ;; --- Group Shape
|
(and (= (:type shape) :group) (gmt/matrix? rz-modifier))
|
||||||
|
(geom/transform rz-modifier)
|
||||||
;; (mx/defc group-shape
|
|
||||||
;; {:mixins [mx/static mx/reactive]}
|
(gmt/matrix? rz-modifier)
|
||||||
;; [{:keys [id items modifiers] :as shape} factory]
|
(-> (geom/transform rz-modifier)
|
||||||
;; (let [{:keys [resize displacement]} modifiers
|
(dissoc :resize-modifier)))]
|
||||||
|
(geom/move shape pt)))
|
||||||
;; xfmt (cond-> (gmt/matrix)
|
|
||||||
;; resize (gmt/multiply resize)
|
|
||||||
;; displacement (gmt/multiply displacement))
|
|
||||||
|
|
||||||
;; moving? (boolean displacement)]
|
|
||||||
;; [:g {:id (str "shape-" id)
|
|
||||||
;; :class (classnames :move-cursor moving?)
|
|
||||||
;; :transform (str xfmt)}
|
|
||||||
;; (for [item (reverse items)]
|
|
||||||
;; (-> (factory item)
|
|
||||||
;; (mx/with-key (str item))))]))
|
|
||||||
|
|
||||||
|
|
52
frontend/src/uxbox/main/ui/shapes/shape.cljs
Normal file
52
frontend/src/uxbox/main/ui/shapes/shape.cljs
Normal file
|
@ -0,0 +1,52 @@
|
||||||
|
;; 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) 2020 UXBOX Labs SL
|
||||||
|
|
||||||
|
(ns uxbox.main.ui.shapes.shape
|
||||||
|
(:require
|
||||||
|
[rumext.alpha :as mf]
|
||||||
|
[uxbox.main.ui.shapes.circle :as circle]
|
||||||
|
[uxbox.main.ui.shapes.common :as common]
|
||||||
|
[uxbox.main.ui.shapes.icon :as icon]
|
||||||
|
[uxbox.main.ui.shapes.image :as image]
|
||||||
|
[uxbox.main.ui.shapes.path :as path]
|
||||||
|
[uxbox.main.ui.shapes.rect :as rect]
|
||||||
|
[uxbox.main.ui.shapes.text :as text]
|
||||||
|
[uxbox.main.ui.shapes.group :as group]
|
||||||
|
[uxbox.main.ui.shapes.frame :as frame]))
|
||||||
|
|
||||||
|
(defn wrap-memo-shape
|
||||||
|
([component]
|
||||||
|
(mf/memo'
|
||||||
|
component
|
||||||
|
(fn [np op]
|
||||||
|
(let [n-shape (unchecked-get np "shape")
|
||||||
|
o-shape (unchecked-get op "shape")]
|
||||||
|
(= n-shape o-shape))))))
|
||||||
|
|
||||||
|
(declare group-wrapper)
|
||||||
|
(declare frame-wrapper)
|
||||||
|
|
||||||
|
(mf/defc shape-wrapper
|
||||||
|
{::mf/wrap [wrap-memo-shape]}
|
||||||
|
[{:keys [shape] :as props}]
|
||||||
|
(when (and shape (not (:hidden shape)))
|
||||||
|
(case (:type shape)
|
||||||
|
:group [:& group-wrapper {:shape shape}]
|
||||||
|
:curve [:& path/path-wrapper {:shape shape}]
|
||||||
|
:text [:& text/text-wrapper {:shape shape}]
|
||||||
|
:icon [:& icon/icon-wrapper {:shape shape}]
|
||||||
|
:rect [:& rect/rect-wrapper {:shape shape}]
|
||||||
|
:path [:& path/path-wrapper {:shape shape}]
|
||||||
|
:image [:& image/image-wrapper {:shape shape}]
|
||||||
|
:circle [:& circle/circle-wrapper {:shape shape}]
|
||||||
|
:frame [:& frame-wrapper {:shape shape}]
|
||||||
|
nil)))
|
||||||
|
|
||||||
|
(def group-wrapper (group/group-wrapper shape-wrapper))
|
||||||
|
(def frame-wrapper (frame/frame-wrapper shape-wrapper))
|
|
@ -41,6 +41,7 @@
|
||||||
:rect i/box
|
:rect i/box
|
||||||
:curve i/curve
|
:curve i/curve
|
||||||
:text i/text
|
:text i/text
|
||||||
|
:group i/folder
|
||||||
nil))
|
nil))
|
||||||
|
|
||||||
;; --- Layer Name
|
;; --- Layer Name
|
||||||
|
@ -82,8 +83,16 @@
|
||||||
|
|
||||||
(mf/defc layer-item
|
(mf/defc layer-item
|
||||||
{:wrap [mf/wrap-memo]}
|
{:wrap [mf/wrap-memo]}
|
||||||
[{:keys [index item selected] :as props}]
|
[{:keys [index item selected objects] :as props}]
|
||||||
(let [selected? (contains? selected (:id item))
|
(let [selected? (contains? selected (:id item))
|
||||||
|
local (mf/use-state {:collapsed false})
|
||||||
|
collapsed? (:collapsed @local)
|
||||||
|
|
||||||
|
toggle-collapse
|
||||||
|
(fn [event]
|
||||||
|
(dom/stop-propagation event)
|
||||||
|
(swap! local update :collapsed not))
|
||||||
|
|
||||||
toggle-blocking
|
toggle-blocking
|
||||||
(fn [event]
|
(fn [event]
|
||||||
(dom/stop-propagation event)
|
(dom/stop-propagation event)
|
||||||
|
@ -151,13 +160,30 @@
|
||||||
:on-double-click #(dom/stop-propagation %)}
|
:on-double-click #(dom/stop-propagation %)}
|
||||||
[:& element-icon {:shape item}]
|
[:& element-icon {:shape item}]
|
||||||
[:& layer-name {:shape item}]
|
[:& layer-name {:shape item}]
|
||||||
|
|
||||||
[:div.element-actions
|
[:div.element-actions
|
||||||
[:div.toggle-element {:class (when (:hidden item) "selected")
|
[:div.toggle-element {:class (when (:hidden item) "selected")
|
||||||
:on-click toggle-visibility}
|
:on-click toggle-visibility}
|
||||||
i/eye]
|
i/eye]
|
||||||
[:div.block-element {:class (when (:blocked item) "selected")
|
[:div.block-element {:class (when (:blocked item) "selected")
|
||||||
:on-click toggle-blocking}
|
:on-click toggle-blocking}
|
||||||
i/lock]]]]))
|
i/lock]]
|
||||||
|
|
||||||
|
(when (:shapes item)
|
||||||
|
[:span.toggle-content
|
||||||
|
{:on-click toggle-collapse
|
||||||
|
:class (when-not collapsed? "inverse")}
|
||||||
|
i/arrow-slide])]
|
||||||
|
(when (and (:shapes item) (not collapsed?))
|
||||||
|
[:ul.element-children
|
||||||
|
(for [[index id] (d/enumerate (:shapes item))]
|
||||||
|
(let [item (get objects id)]
|
||||||
|
[:& layer-item
|
||||||
|
{:item item
|
||||||
|
:selected selected
|
||||||
|
:index index
|
||||||
|
:objects objects
|
||||||
|
:key (:id item)}]))])]))
|
||||||
|
|
||||||
(mf/defc layer-frame-item
|
(mf/defc layer-frame-item
|
||||||
{:wrap [#(mf/wrap-memo % =)]}
|
{:wrap [#(mf/wrap-memo % =)]}
|
||||||
|
@ -252,18 +278,12 @@
|
||||||
[:ul
|
[:ul
|
||||||
(for [[index id] (d/enumerate (reverse (:shapes item)))]
|
(for [[index id] (d/enumerate (reverse (:shapes item)))]
|
||||||
(let [item (get objects id)]
|
(let [item (get objects id)]
|
||||||
(if (= (:type item) :frame)
|
[:& layer-item
|
||||||
[:& layer-frame-item
|
{:item item
|
||||||
{:item item
|
:selected selected
|
||||||
:key (:id item)
|
:index index
|
||||||
:selected selected
|
:objects objects
|
||||||
:objects objects
|
:key (:id item)}]))])]))
|
||||||
:index index}]
|
|
||||||
[:& layer-item
|
|
||||||
{:item item
|
|
||||||
:selected selected
|
|
||||||
:index index
|
|
||||||
:key (:id item)}])))])]))
|
|
||||||
|
|
||||||
(mf/defc layers-tree
|
(mf/defc layers-tree
|
||||||
{::mf/wrap [mf/wrap-memo]}
|
{::mf/wrap [mf/wrap-memo]}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue