🔧 Read component shapes from pages

This commit is contained in:
Andrés Moya 2023-03-07 12:03:38 +01:00
parent a4dd5fccff
commit 0711fa700b
21 changed files with 588 additions and 403 deletions

View file

@ -492,10 +492,13 @@
(library-summary [{:keys [id data] :as file}] (library-summary [{:keys [id data] :as file}]
(binding [pmap/*load-fn* (partial load-pointer conn id)] (binding [pmap/*load-fn* (partial load-pointer conn id)]
{:components (assets-sample (:components data) 4) (let [components-sample (-> (assets-sample (:components data) 4)
(update :sample
#(map (partial ctf/load-component-objects data) %)))]
{:components components-sample
:media (assets-sample (:media data) 3) :media (assets-sample (:media data) 3)
:colors (assets-sample (:colors data) 3) :colors (assets-sample (:colors data) 3)
:typographies (assets-sample (:typographies data) 3)}))] :typographies (assets-sample (:typographies data) 3)})))]
(->> (db/exec! conn [sql:team-shared-files team-id]) (->> (db/exec! conn [sql:team-shared-files team-id])
(into #{} (comp (into #{} (comp
@ -552,7 +555,10 @@
(map (fn [{:keys [id] :as row}] (map (fn [{:keys [id] :as row}]
(binding [pmap/*load-fn* (partial load-pointer conn id)] (binding [pmap/*load-fn* (partial load-pointer conn id)]
(-> row (-> row
(update :data dissoc :pages-index) ;; TODO: re-enable this dissoc and replace call
;; with other that gets files individually
;; See task https://tree.taiga.io/project/penpot/task/4904
;; (update :data dissoc :pages-index)
(handle-file-features client-features))))) (handle-file-features client-features)))))
(vec))) (vec)))

View file

@ -8,6 +8,7 @@
"A version parsing helper." "A version parsing helper."
(:require (:require
[app.common.data :as d] [app.common.data :as d]
[app.common.data.macros :as dm]
[app.common.exceptions :as ex] [app.common.exceptions :as ex]
[app.common.geom.matrix :as gmt] [app.common.geom.matrix :as gmt]
[app.common.geom.point :as gpt] [app.common.geom.point :as gpt]
@ -664,9 +665,10 @@
[_ shapes] [_ shapes]
(ctn/make-component-instance page (ctn/make-component-instance page
component component
(:id file) (:data file)
(gpt/point main-instance-x (gpt/point main-instance-x
main-instance-y) main-instance-y)
true
{:main-instance? true {:main-instance? true
:force-id main-instance-id})] :force-id main-instance-id})]
(as-> file $ (as-> file $
@ -701,12 +703,15 @@
component (ctkl/get-component (:data file) component-id) component (ctkl/get-component (:data file) component-id)
;; main-instance-id (:main-instance-id component) ;; main-instance-id (:main-instance-id component)
components-v2 (dm/get-in file [:options :components-v2])
[shape shapes] [shape shapes]
(ctn/make-component-instance page (ctn/make-component-instance page
component component
(:id file) (:id file)
(gpt/point x (gpt/point x
y) y)
components-v2
#_{:main-instance? true #_{:main-instance? true
:force-id main-instance-id})] :force-id main-instance-id})]

View file

@ -15,6 +15,7 @@
[app.common.math :as mth] [app.common.math :as mth]
[app.common.pages :as cp] [app.common.pages :as cp]
[app.common.pages.helpers :as cph] [app.common.pages.helpers :as cph]
[app.common.types.components-list :as ctkl]
[app.common.types.file :as ctf] [app.common.types.file :as ctf]
[app.common.uuid :as uuid])) [app.common.uuid :as uuid]))
@ -598,13 +599,14 @@
(update :redo-changes (update :redo-changes
(fn [redo-changes] (fn [redo-changes]
(-> redo-changes (-> redo-changes
(conj {:type :add-component (conj (cond-> {:type :add-component
:id id :id id
:path path :path path
:name name :name name
:main-instance-id main-instance-id :main-instance-id main-instance-id
:main-instance-page main-instance-page :main-instance-page main-instance-page}
:shapes new-shapes}) (some? new-shapes) ;; this will be null in components-v2
(assoc :shapes new-shapes)))
(into (map mk-change) updated-shapes)))) (into (map mk-change) updated-shapes))))
(update :undo-changes (update :undo-changes
(fn [undo-changes] (fn [undo-changes]
@ -641,7 +643,8 @@
[changes id components-v2] [changes id components-v2]
(assert-library changes) (assert-library changes)
(let [library-data (::library-data (meta changes)) (let [library-data (::library-data (meta changes))
prev-component (get-in library-data [:components id])] component (ctkl/get-component library-data id)
shapes (ctf/get-component-shapes library-data component)]
(-> changes (-> changes
(update :redo-changes conj {:type :del-component (update :redo-changes conj {:type :del-component
:id id}) :id id})
@ -655,11 +658,11 @@
:always :always
(d/preconj {:type :add-component (d/preconj {:type :add-component
:id id :id id
:name (:name prev-component) :name (:name component)
:path (:path prev-component) :path (:path component)
:main-instance-id (:main-instance-id prev-component) :main-instance-id (:main-instance-id component)
:main-instance-page (:main-instance-page prev-component) :main-instance-page (:main-instance-page component)
:shapes (vals (:objects prev-component))}))))))) :shapes shapes})))))))
(defn restore-component (defn restore-component
[changes id] [changes id]

View file

@ -153,8 +153,8 @@
(s/coll-of ::cts/shape)) (s/coll-of ::cts/shape))
(defmethod change-spec :add-component [_] (defmethod change-spec :add-component [_]
(s/keys :req-un [::id ::name :internal.changes.add-component/shapes] (s/keys :req-un [::id ::name]
:opt-un [::path])) :opt-un [::path :internal.changes.add-component/shapes]))
(defmethod change-spec :mod-component [_] (defmethod change-spec :mod-component [_]
(s/keys :req-un [::id] (s/keys :req-un [::id]

View file

@ -270,25 +270,6 @@
[shape group] [shape group]
((or (:touched shape) #{}) group)) ((or (:touched shape) #{}) group))
(defn get-component
"Retrieve a component from libraries, if no library-id is provided, we
iterate over all libraries and find the component on it."
([libraries component-id]
(some #(-> % :data :components (get component-id)) (vals libraries)))
([libraries library-id component-id]
(get-in libraries [library-id :data :components component-id])))
(defn get-component-shape
"Get the parent shape linked to a component for this shape, if any"
[objects shape]
(if-not (:shape-ref shape)
nil
(if (:component-id shape)
shape
(if-let [parent-id (:parent-id shape)]
(get-component-shape objects (get objects parent-id))
nil))))
(defn get-root-shape (defn get-root-shape
"Get the root shape linked to a component for this shape, if any." "Get the root shape linked to a component for this shape, if any."
[objects shape] [objects shape]

View file

@ -34,6 +34,7 @@
(and (= shape-id (:main-instance-id component)) (and (= shape-id (:main-instance-id component))
(= page-id (:main-instance-page component)))) (= page-id (:main-instance-page component))))
;; Obsolete in components-v2
(defn get-component-root (defn get-component-root
[component] [component]
(get-in component [:objects (:id component)])) (get-in component [:objects (:id component)]))
@ -45,12 +46,12 @@
(= (:component-file shape) library-id))) (= (:component-file shape) library-id)))
(defn in-component-instance? (defn in-component-instance?
"Check if the shape is inside a component instance." "Check if the shape is inside a component non-main instance."
[shape] [shape]
(some? (:shape-ref shape))) (some? (:shape-ref shape)))
(defn in-component-instance-not-root? (defn in-component-instance-not-root?
"Check if the shape is inside a component instance and "Check if the shape is inside a component non-main instance and
is not the root shape." is not the root shape."
[shape] [shape]
(and (some? (:shape-ref shape)) (and (some? (:shape-ref shape))

View file

@ -23,11 +23,13 @@
(assoc-in [:components id] (assoc-in [:components id]
{:id id {:id id
:name name :name name
:path path :path path})
:objects (->> shapes
(d/index-by :id)
(wrap-object-fn))})
(not components-v2)
(assoc-in [:components id :objects]
(->> shapes
(d/index-by :id)
(wrap-object-fn)))
components-v2 components-v2
(update-in [:components id] assoc (update-in [:components id] assoc
:main-instance-id main-instance-id :main-instance-id main-instance-id
@ -60,4 +62,3 @@
(defn delete-component (defn delete-component
[file-data component-id] [file-data component-id]
(update file-data :components dissoc component-id)) (update file-data :components dissoc component-id))

View file

@ -11,6 +11,7 @@
[app.common.geom.shapes :as gsh] [app.common.geom.shapes :as gsh]
[app.common.pages.common :as common] [app.common.pages.common :as common]
[app.common.spec :as us] [app.common.spec :as us]
[app.common.types.pages-list :as ctpl]
[app.common.types.shape-tree :as ctst] [app.common.types.shape-tree :as ctst]
[clojure.spec.alpha :as s])) [clojure.spec.alpha :as s]))
@ -62,6 +63,17 @@
[container shape-id f] [container shape-id f]
(update-in container [:objects shape-id] f)) (update-in container [:objects shape-id] f))
(defn get-component-shape
"Get the parent shape linked to a component for this shape, if any"
[objects shape]
(if-not (:shape-ref shape)
nil
(if (:component-id shape)
shape
(if-let [parent-id (:parent-id shape)]
(get-component-shape objects (get objects parent-id))
nil))))
(defn make-component-shape (defn make-component-shape
"Clone the shape and all children. Generate new ids and detach "Clone the shape and all children. Generate new ids and detach
from parent and frame. Update the original shapes to have links from parent and frame. Update the original shapes to have links
@ -117,15 +129,22 @@
[new-root-shape (map remap-frame-id new-shapes) updated-shapes])) [new-root-shape (map remap-frame-id new-shapes) updated-shapes]))
(defn make-component-instance (defn make-component-instance
"Clone the shapes of the component, generating new names and ids, and linking "Generate a new instance of the component inside the given container.
Clone the shapes of the component, generating new names and ids, and linking
each new shape to the corresponding one of the component. Place the new instance each new shape to the corresponding one of the component. Place the new instance
coordinates in the given position." coordinates in the given position."
([container component component-file-id position] ([container component library-data position components-v2]
(make-component-instance container component component-file-id position {})) (make-component-instance container component library-data position components-v2 {}))
([container component component-file-id position ([container component library-data position components-v2
{:keys [main-instance? force-id] :or {main-instance? false force-id nil}}] {:keys [main-instance? force-id] :or {main-instance? false force-id nil}}]
(let [component-shape (get-shape component (:id component)) (let [component-page (when components-v2
(ctpl/get-page library-data (:main-instance-page component)))
component-shape (if components-v2
(-> (get-shape component-page (:main-instance-id component))
(assoc :parent-id nil))
(get-shape component (:id component)))
orig-pos (gpt/point (:x component-shape) (:y component-shape)) orig-pos (gpt/point (:x component-shape) (:y component-shape))
delta (gpt/subtract position orig-pos) delta (gpt/subtract position orig-pos)
@ -147,20 +166,20 @@
(vswap! frame-ids-map assoc (:id original-shape) (:id new-shape))) (vswap! frame-ids-map assoc (:id original-shape) (:id new-shape)))
(cond-> new-shape (cond-> new-shape
true :always
(-> (gsh/move delta) (-> (gsh/move delta)
(dissoc :touched)) (dissoc :touched :main-instance?))
(nil? (:shape-ref original-shape)) (nil? (:shape-ref original-shape))
(assoc :shape-ref (:id original-shape)) (assoc :shape-ref (:id original-shape))
(nil? (:parent-id original-shape)) (nil? (:parent-id original-shape))
(assoc :component-id (:id original-shape) (assoc :component-id (:id component)
:component-file component-file-id :component-file (:id library-data)
:component-root? true :component-root? true
:name new-name) :name new-name)
(and (nil? (:parent-id original-shape)) main-instance?) (and (nil? (:parent-id original-shape)) main-instance? components-v2)
(assoc :main-instance? true) (assoc :main-instance? true)
(some? (:parent-id original-shape)) (some? (:parent-id original-shape))
@ -169,7 +188,7 @@
[new-shape new-shapes _] [new-shape new-shapes _]
(ctst/clone-object component-shape (ctst/clone-object component-shape
nil nil
(get component :objects) (if components-v2 (:objects component-page) (:objects component))
update-new-shape update-new-shape
(fn [object _] object) (fn [object _] object)
force-id) force-id)

View file

@ -116,6 +116,75 @@
([libraries library-id component-id] ([libraries library-id component-id]
(ctkl/get-component (dm/get-in libraries [library-id :data]) component-id))) (ctkl/get-component (dm/get-in libraries [library-id :data]) component-id)))
(defn get-component-library
"Retrieve the library the component belongs to."
[libraries instance-root]
(get libraries (:component-file instance-root)))
(defn get-component-page
"Retrieve the page where the main instance of the component resides."
[file-data component]
(ctpl/get-page file-data (:main-instance-page component)))
(defn get-component-container
"Retrieve the container that holds the component shapes (the page in components-v2
or the component itself in v1)"
[file-data component]
(let [components-v2 (dm/get-in file-data [:options :components-v2])]
(if components-v2
(let [component-page (get-component-page file-data component)]
(cph/make-container component-page :page))
(cph/make-container component :component))))
(defn get-component-root
"Retrieve the root shape of the component."
[file-data component]
(let [components-v2 (dm/get-in file-data [:options :components-v2])]
(if components-v2
(-> file-data
(get-component-page component)
(ctn/get-shape (:main-instance-id component)))
(ctk/get-component-root component))))
(defn get-component-shapes
"Retrieve all shapes of the component"
[file-data component]
(let [components-v2 (dm/get-in file-data [:options :components-v2])]
(if components-v2
(let [instance-page (get-component-page file-data component)]
(cph/get-children-with-self (:objects instance-page) (:main-instance-id component)))
(vals (:objects component)))))
(defn get-component-shape
"Retrieve one shape in the component."
[file-data component shape-id]
(let [components-v2 (dm/get-in file-data [:options :components-v2])]
(if components-v2
(let [component-page (get-component-page file-data component)]
(ctn/get-shape component-page shape-id))
(dm/get-in component [:objects shape-id]))))
(defn get-ref-shape
"Retrieve the shape in the component that is referenced by the
instance shape."
[file-data component shape]
(when (:shape-ref shape)
(get-component-shape file-data component (:shape-ref shape))))
(defn load-component-objects
"Add an :objects property to the component, with only the shapes that belong to it"
[file-data component]
(let [components-v2 (dm/get-in file-data [:options :components-v2])]
(if components-v2
(let [component-page (get-component-page file-data component)
page-objects (:objects component-page)
objects (->> (cons (:main-instance-id component)
(cph/get-children-ids page-objects (:main-instance-id component)))
(map #(get page-objects %))
(d/index-by :id))]
(assoc component :objects objects))
component)))
(defn delete-component (defn delete-component
"Delete a component and store it to be able to be recovered later. "Delete a component and store it to be able to be recovered later.
@ -123,26 +192,29 @@
([file-data component-id] ([file-data component-id]
(delete-component file-data component-id false)) (delete-component file-data component-id false))
([file-data component-id skip-undelete?] ([file-data component-id _skip-undelete?]
(let [components-v2 (dm/get-in file-data [:options :components-v2]) (let [_components-v2 (dm/get-in file-data [:options :components-v2])
add-to-deleted-components ;; TODO: replace :deleted-components with a :deleted? flag in normal shapes
(fn [file-data] ;; see task https://tree.taiga.io/project/penpot/task/4998
(let [component (ctkl/get-component file-data component-id)] ;;
(if (some? component) ;; add-to-deleted-components
(let [page (ctpl/get-page file-data (:main-instance-page component)) ;; (fn [file-data]
main-instance (ctn/get-shape page (:main-instance-id component)) ;; (let [component (ctkl/get-component file-data component-id)]
component (assoc component ;; (if (some? component)
:main-instance-x (:x main-instance) ; An instance root is always a group, ;; (let [main-instance (get-component-root file-data component)
:main-instance-y (:y main-instance))] ; so it will have :x and :y ;; component (assoc component
(when (nil? main-instance) ;; :main-instance-x (:x main-instance) ; An instance root is always a group,
(throw (ex-info "Cannot delete the main instance before the component" {:component-id component-id}))) ;; :main-instance-y (:y main-instance))] ; or a frame, so it will have :x and :y
(assoc-in file-data [:deleted-components component-id] component)) ;; (when (nil? main-instance)
file-data)))] ;; (throw (ex-info "Cannot delete the main instance before the component" {:component-id component-id})))
;; (assoc-in file-data [:deleted-components component-id] component))
;; file-data)))
]
(cond-> file-data (cond-> file-data
(and components-v2 (not skip-undelete?)) ;; (and components-v2 (not skip-undelete?))
(add-to-deleted-components) ;; (add-to-deleted-components)
:always :always
(ctkl/delete-component component-id))))) (ctkl/delete-component component-id)))))
@ -243,8 +315,9 @@
[(ctpl/add-page file-data library-page) (:id library-page) (gpt/point 0 0)])))) [(ctpl/add-page file-data library-page) (:id library-page) (gpt/point 0 0)]))))
(defn migrate-to-components-v2 (defn migrate-to-components-v2
"If there is any component in the file library, add a new 'Library backup' and generate "If there is any component in the file library, add a new 'Library backup', generate
main instances for all components there. Mark the file with the :components-v2 option." main instances for all components there and remove shapes from library components.
Mark the file with the :components-v2 option."
[file-data] [file-data]
(let [components (ctkl/components-seq file-data)] (let [components (ctkl/components-seq file-data)]
(if (or (empty? components) (if (or (empty? components)
@ -262,8 +335,9 @@
[new-shape new-shapes] [new-shape new-shapes]
(ctn/make-component-instance page (ctn/make-component-instance page
component component
(:id file-data) file-data
position position
false
{:main-instance? true}) {:main-instance? true})
add-shapes add-shapes
@ -281,9 +355,10 @@
update-component update-component
(fn [component] (fn [component]
(assoc component (-> component
:main-instance-id (:id new-shape) (assoc :main-instance-id (:id new-shape)
:main-instance-page page-id))] :main-instance-page page-id)
(dissoc :objects)))]
(-> file-data (-> file-data
(ctpl/update-page page-id add-shapes) (ctpl/update-page page-id add-shapes)
@ -292,7 +367,7 @@
add-instance-grid add-instance-grid
(fn [file-data components] (fn [file-data components]
(let [position-seq (ctst/generate-shape-grid (let [position-seq (ctst/generate-shape-grid
(map ctk/get-component-root components) (map (partial get-component-root file-data) components)
start-pos start-pos
grid-gap)] grid-gap)]
(loop [file-data file-data (loop [file-data file-data
@ -311,7 +386,7 @@
(assoc-in [:options :components-v2] true)))))) (assoc-in [:options :components-v2] true))))))
(defn- absorb-components (defn- absorb-components
[file-data used-components] [file-data used-components library-data]
(let [grid-gap 50 (let [grid-gap 50
; Search for the library page. If not exists, create it. ; Search for the library page. If not exists, create it.
@ -326,10 +401,17 @@
[main-instance-shape main-instance-shapes] [main-instance-shape main-instance-shapes]
(ctn/make-component-instance page (ctn/make-component-instance page
component component
(:id file-data) library-data
position position
(dm/get-in file-data [:options :components-v2])
{:main-instance? true}) {:main-instance? true})
main-instance-shapes
(map #(cond-> %
(some? (:component-file %))
(assoc :component-file (:id file-data)))
main-instance-shapes)
; Add all shapes of the main instance to the library page ; Add all shapes of the main instance to the library page
add-main-instance-shapes add-main-instance-shapes
(fn [page] (fn [page]
@ -353,7 +435,7 @@
:path (:path component) :path (:path component)
:main-instance-id (:id main-instance-shape) :main-instance-id (:id main-instance-shape)
:main-instance-page page-id :main-instance-page page-id
:shapes (vals (:objects component))})) :shapes (get-component-shapes library-data component)}))
; Change all existing instances to point to the local file ; Change all existing instances to point to the local file
remap-instances remap-instances
@ -378,7 +460,7 @@
add-component-grid add-component-grid
(fn [data used-components] (fn [data used-components]
(let [position-seq (ctst/generate-shape-grid (let [position-seq (ctst/generate-shape-grid
(map #(ctk/get-component-root (first %)) used-components) (map #(get-component-root library-data (first %)) used-components)
start-pos start-pos
grid-gap)] grid-gap)]
(loop [data data (loop [data data
@ -452,7 +534,7 @@
(cond-> file-data (cond-> file-data
(d/not-empty? used-components) (d/not-empty? used-components)
(absorb-components used-components) (absorb-components used-components library-data)
(d/not-empty? used-colors) (d/not-empty? used-colors)
(absorb-colors used-colors) (absorb-colors used-colors)
@ -479,12 +561,14 @@
(letfn [(show-shape [shape-id level objects] (letfn [(show-shape [shape-id level objects]
(let [shape (get objects shape-id)] (let [shape (get objects shape-id)]
(println (str/pad (str (str/repeat " " level) (println (str/pad (str (str/repeat " " level)
(when (:main-instance? shape) "{")
(:name shape) (:name shape)
(when (:main-instance? shape) "}")
(when (seq (:touched shape)) "*") (when (seq (:touched shape)) "*")
(when show-ids (str/format " <%s>" (:id shape)))) (when show-ids (str/format " <%s>" (:id shape))))
{:length 20 {:length 20
:type :right}) :type :right})
(show-component shape objects)) (show-component-info shape objects))
(when show-touched (when show-touched
(when (seq (:touched shape)) (when (seq (:touched shape))
(println (str (str/repeat " " level) (println (str (str/repeat " " level)
@ -497,10 +581,10 @@
(dorun (for [shape-id (:shapes shape)] (dorun (for [shape-id (:shapes shape)]
(show-shape shape-id (inc level) objects)))))) (show-shape shape-id (inc level) objects))))))
(show-component [shape objects] (show-component-info [shape objects]
(if (nil? (:shape-ref shape)) (if (nil? (:shape-ref shape))
"" (if (:component-root? shape) " #" "")
(let [root-shape (cph/get-component-shape objects shape) (let [root-shape (ctn/get-component-shape objects shape)
component-id (when root-shape (:component-id root-shape)) component-id (when root-shape (:component-id root-shape))
component-file-id (when root-shape (:component-file root-shape)) component-file-id (when root-shape (:component-file root-shape))
component-file (when component-file-id (get libraries component-file-id nil)) component-file (when component-file-id (get libraries component-file-id nil))
@ -508,8 +592,11 @@
(if component-file (if component-file
(dm/get-in component-file [:data :components component-id]) (dm/get-in component-file [:data :components component-id])
(get components component-id))) (get components component-id)))
component-shape (when (and component (:shape-ref shape)) component-shape (when component
(dm/get-in component [:objects (:shape-ref shape)]))] (if component-file
(get-ref-shape (:data component-file) component shape)
(get-ref-shape file-data component shape)))]
(str/format " %s--> %s%s%s" (str/format " %s--> %s%s%s"
(cond (:component-root? shape) "#" (cond (:component-root? shape) "#"
(:component-id shape) "@" (:component-id shape) "@"
@ -528,18 +615,26 @@
(get components component-id))] (get components component-id))]
(str/format " (%s%s)" (str/format " (%s%s)"
(when component-file (str/format "<%s> " (:name component-file))) (when component-file (str/format "<%s> " (:name component-file)))
(:name component))))))))] (:name component))))))))
(println "[Page]") (show-component-instance [component]
(let [page (get-component-page file-data component)
root (get-component-root file-data component)]
(if-not show-ids
(println (str " [" (:name page) "] / " (:name root)))
(do
(println (str " " (:name page) (str/format " <%s>" (:id page))))
(println (str " " (:name root) (str/format " <%s>" (:id root))))))))]
(println (str "[Page: " (:name page) "]"))
(show-shape (:id root) 0 objects) (show-shape (:id root) 0 objects)
(dorun (for [component (vals components)] (dorun (for [component (vals components)]
(do (do
(println) (println)
(println (str/format "[%s]" (:name component)) (println (str/format "[%s]" (:name component)))
(when show-ids (when (:objects component)
(str/format " (main: %s/%s)" (show-shape (:id component) 0 (:objects component)))
(:main-instance-page component) (when (:main-instance-page component)
(:main-instance-id component)))) (show-component-instance component)))))))))
(show-shape (:id component) 0 (:objects component)))))))))

View file

@ -359,4 +359,3 @@
(iterate next-pos (iterate next-pos
(with-meta start-pos (with-meta start-pos
{:counter 0})))) {:counter 0}))))

View file

@ -9,7 +9,8 @@
[clojure.test :as t] [clojure.test :as t]
[app.common.pages.helpers :as cph] [app.common.pages.helpers :as cph]
[app.common.types.component :as ctk] [app.common.types.component :as ctk]
[app.common.types.container :as ctn])) [app.common.types.container :as ctn]
[app.common.types.file :as ctf]))
;; ---- Helpers to manage libraries and synchronization ;; ---- Helpers to manage libraries and synchronization
@ -81,7 +82,7 @@
[page root-inst-id libraries] [page root-inst-id libraries]
(let [root-inst (ctn/get-shape page root-inst-id) (let [root-inst (ctn/get-shape page root-inst-id)
component (cph/get-component libraries (:component-id root-inst)) component (ctf/get-component libraries (:component-id root-inst))
shapes-inst (cph/get-children-with-self (:objects page) root-inst-id) shapes-inst (cph/get-children-with-self (:objects page) root-inst-id)
shapes-main (cph/get-children-with-self (:objects component) (:shape-ref root-inst)) shapes-main (cph/get-children-with-self (:objects component) (:shape-ref root-inst))
@ -90,10 +91,10 @@
main-exists? (fn [shape] main-exists? (fn [shape]
(let [component-shape (let [component-shape
(cph/get-component-shape (:objects page) shape) (ctn/get-component-shape (:objects page) shape)
component component
(cph/get-component libraries (:component-id component-shape)) (ctf/get-component libraries (:component-id component-shape))
main-shape main-shape
(ctn/get-shape component (:shape-ref shape))] (ctn/get-shape component (:shape-ref shape))]
@ -117,7 +118,7 @@
[page root-inst-id libraries] [page root-inst-id libraries]
(let [root-inst (ctn/get-shape page root-inst-id) (let [root-inst (ctn/get-shape page root-inst-id)
component (cph/get-component libraries (:component-id root-inst)) component (ctf/get-component libraries (:component-id root-inst))
shapes-inst (cph/get-children-with-self (:objects page) root-inst-id) shapes-inst (cph/get-children-with-self (:objects page) root-inst-id)
shapes-main (cph/get-children-with-self (:objects component) (:shape-ref root-inst)) shapes-main (cph/get-children-with-self (:objects component) (:shape-ref root-inst))
@ -126,10 +127,10 @@
main-exists? (fn [shape] main-exists? (fn [shape]
(let [component-shape (let [component-shape
(cph/get-component-shape (:objects page) shape) (ctn/get-component-shape (:objects page) shape)
component component
(cph/get-component libraries (:component-id component-shape)) (ctf/get-component libraries (:component-id component-shape))
main-shape main-shape
(ctn/get-shape component (:shape-ref shape))] (ctn/get-shape component (:shape-ref shape))]
@ -144,7 +145,7 @@
(defn resolve-component (defn resolve-component
"Get the component with the given id and all its shapes." "Get the component with the given id and all its shapes."
[page component-id libraries] [page component-id libraries]
(let [component (cph/get-component libraries component-id) (let [component (ctf/get-component libraries component-id)
root-main (ctk/get-component-root component) root-main (ctk/get-component-root component)
shapes-main (cph/get-children-with-self (:objects component) (:id root-main))] shapes-main (cph/get-children-with-self (:objects component) (:id root-main))]

View file

@ -102,8 +102,9 @@
(let [[instance-shape instance-shapes] (let [[instance-shape instance-shapes]
(ctn/make-component-instance (ctpl/get-page file-data page-id) (ctn/make-component-instance (ctpl/get-page file-data page-id)
(ctkl/get-component (:data library) component-id) (ctkl/get-component (:data library) component-id)
(:id library) (:data library)
(gpt/point 0 0))] (gpt/point 0 0)
true)]
(swap! idmap assoc label (:id instance-shape)) (swap! idmap assoc label (:id instance-shape))
(-> file-data (-> file-data

View file

@ -233,6 +233,19 @@
(->> (filter (comp t/pointer? val) data) (->> (filter (comp t/pointer? val) data)
(resolve-pointers id) (resolve-pointers id)
(rx/map #(update file :data merge %))))) (rx/map #(update file :data merge %)))))
(rx/mapcat
(fn [{:keys [id data] :as file}]
;; Resolve all pages of each library, if needed
(->> (rx/from (seq (:pages-index data)))
(rx/merge-map
(fn [[_ page :as kp]]
(if (t/pointer? page)
(resolve-pointer id kp)
(rx/of kp))))
(rx/reduce conj {})
(rx/map
(fn [pages-index]
(assoc-in file [:data :pages-index] pages-index))))))
(rx/reduce conj []) (rx/reduce conj [])
(rx/map libraries-fetched)))))))) (rx/map libraries-fetched))))))))
@ -808,8 +821,8 @@
(not (:component-root? shape))) (not (:component-root? shape)))
parent (get objects parent-id) parent (get objects parent-id)
component-shape (cph/get-component-shape objects shape) component-shape (ctn/get-component-shape objects shape)
component-shape-parent (cph/get-component-shape objects parent) component-shape-parent (ctn/get-component-shape objects parent)
detach? (and instance-part? (not= (:id component-shape) detach? (and instance-part? (not= (:id component-shape)
(:id component-shape-parent))) (:id component-shape-parent)))

View file

@ -17,6 +17,7 @@
[app.common.pages.helpers :as cph] [app.common.pages.helpers :as cph]
[app.common.spec :as us] [app.common.spec :as us]
[app.common.types.color :as ctc] [app.common.types.color :as ctc]
[app.common.types.component :as ctk]
[app.common.types.components-list :as ctkl] [app.common.types.components-list :as ctkl]
[app.common.types.container :as ctn] [app.common.types.container :as ctn]
[app.common.types.file :as ctf] [app.common.types.file :as ctf]
@ -348,6 +349,7 @@
(when (and (some? new-name) (not= "" new-name)) (when (and (some? new-name) (not= "" new-name))
(let [data (get state :workspace-data) (let [data (get state :workspace-data)
[path name] (cph/parse-path-name new-name) [path name] (cph/parse-path-name new-name)
components-v2 (features/active-feature? state :components-v2)
update-fn update-fn
(fn [component] (fn [component]
@ -355,9 +357,12 @@
;; because there are small possibilities of race ;; because there are small possibilities of race
;; conditions with component deletion. ;; conditions with component deletion.
(when component (when component
(-> component (cond-> component
(assoc :path path) :always
(assoc :name name) (assoc :path path
:name name)
(not components-v2)
(update :objects (update :objects
;; Give the same name to the root shape ;; Give the same name to the root shape
#(assoc-in % [id :name] name))))) #(assoc-in % [id :name] name)))))
@ -370,30 +375,33 @@
(defn duplicate-component (defn duplicate-component
"Create a new component copied from the one with the given id." "Create a new component copied from the one with the given id."
[{:keys [id] :as params}] [library-id component-id]
(ptk/reify ::duplicate-component (ptk/reify ::duplicate-component
ptk/WatchEvent ptk/WatchEvent
(watch [it state _] (watch [it state _]
(let [libraries (wsh/get-libraries state) (let [libraries (wsh/get-libraries state)
component (cph/get-component libraries id) library (get libraries library-id)
component (ctkl/get-component (:data library) component-id)
new-name (:name component) new-name (:name component)
components-v2 (features/active-feature? state :components-v2) components-v2 (features/active-feature? state :components-v2)
main-instance-page (when components-v2 main-instance-page (when components-v2
(wsh/lookup-page state (:main-instance-page component))) (ctf/get-component-page (:data library) component))
main-instance-shape (when components-v2
(ctn/get-shape main-instance-page (:main-instance-id component)))
[new-component-shape new-component-shapes new-component (assoc component :id (uuid/next))
[new-component-shape new-component-shapes ; <- null in components-v2
new-main-instance-shape new-main-instance-shapes] new-main-instance-shape new-main-instance-shapes]
(dwlh/duplicate-component component main-instance-page main-instance-shape) (dwlh/duplicate-component new-component (:data library))
changes (-> (pcb/empty-changes it nil) changes (-> (pcb/empty-changes it nil)
(pcb/with-page main-instance-page) (pcb/with-page main-instance-page)
(pcb/with-objects (:objects main-instance-page)) (pcb/with-objects (:objects main-instance-page))
(pcb/add-objects new-main-instance-shapes {:ignore-touched true}) (pcb/add-objects new-main-instance-shapes {:ignore-touched true})
(pcb/add-component (:id new-component-shape) (pcb/add-component (if components-v2
(:id new-component)
(:id new-component-shape))
(:path component) (:path component)
new-name new-name
new-component-shapes new-component-shapes
@ -429,17 +437,22 @@
(ptk/reify ::restore-component (ptk/reify ::restore-component
ptk/WatchEvent ptk/WatchEvent
(watch [it state _] (watch [it state _]
(assert "Restore component not implemented") ; until we allow a :deleted flag in shapes
(let [file-data (wsh/get-file state library-id) (let [file-data (wsh/get-file state library-id)
component (ctf/get-deleted-component file-data component-id) component (ctf/get-deleted-component file-data component-id)
page (ctpl/get-page file-data (:main-instance-page component)) page (ctpl/get-page file-data (:main-instance-page component))
components-v2
(features/active-feature? state :components-v2)
; Make a new main instance, with the same id of the original ; Make a new main instance, with the same id of the original
[_main-instance shapes] [_main-instance shapes]
(ctn/make-component-instance page (ctn/make-component-instance page
component component
(:id file-data) file-data
(gpt/point (:main-instance-x component) (gpt/point (:main-instance-x component)
(:main-instance-y component)) (:main-instance-y component))
components-v2
{:main-instance? true {:main-instance? true
:force-id (:main-instance-id component)}) :force-id (:main-instance-id component)})
@ -601,12 +614,12 @@
(watch [it state _] (watch [it state _]
(log/info :msg "UPDATE-COMPONENT of shape" :id (str id)) (log/info :msg "UPDATE-COMPONENT of shape" :id (str id))
(let [page-id (get state :current-page-id) (let [page-id (get state :current-page-id)
local-file (wsh/get-local-file state) local-file (wsh/get-local-file state)
libraries (wsh/get-libraries state)
container (cph/get-container local-file :page page-id) container (cph/get-container local-file :page page-id)
shape (ctn/get-shape container id) shape (ctn/get-shape container id)]
(when (ctk/in-component-instance? shape)
(let [libraries (wsh/get-libraries state)
changes changes
(-> (pcb/empty-changes it) (-> (pcb/empty-changes it)
@ -646,7 +659,7 @@
:file-id (:id local-file)))) :file-id (:id local-file))))
(when (seq (:redo-changes nonlocal-changes)) (when (seq (:redo-changes nonlocal-changes))
(dch/commit-changes (assoc nonlocal-changes (dch/commit-changes (assoc nonlocal-changes
:file-id file-id)))))))) :file-id file-id))))))))))
(defn update-component-sync (defn update-component-sync
[shape-id file-id] [shape-id file-id]

View file

@ -7,6 +7,7 @@
(ns app.main.data.workspace.libraries-helpers (ns app.main.data.workspace.libraries-helpers
(:require (:require
[app.common.data :as d] [app.common.data :as d]
[app.common.data.macros :as dm]
[app.common.geom.point :as gpt] [app.common.geom.point :as gpt]
[app.common.geom.shapes :as gsh] [app.common.geom.shapes :as gsh]
[app.common.logging :as log] [app.common.logging :as log]
@ -17,10 +18,12 @@
[app.common.text :as txt] [app.common.text :as txt]
[app.common.types.color :as ctc] [app.common.types.color :as ctc]
[app.common.types.component :as ctk] [app.common.types.component :as ctk]
[app.common.types.components-list :as ctkl]
[app.common.types.container :as ctn] [app.common.types.container :as ctn]
[app.common.types.file :as ctf] [app.common.types.file :as ctf]
[app.common.types.shape-tree :as ctst] [app.common.types.shape-tree :as ctst]
[app.common.types.typography :as cty] [app.common.types.typography :as cty]
[app.common.uuid :as uuid]
[app.main.data.workspace.groups :as dwg] [app.main.data.workspace.groups :as dwg]
[app.main.data.workspace.state-helpers :as wsh] [app.main.data.workspace.state-helpers :as wsh]
[cljs.spec.alpha :as s] [cljs.spec.alpha :as s]
@ -84,54 +87,75 @@
name (:name group) name (:name group)
[path name] (cph/parse-path-name name) [path name] (cph/parse-path-name name)
[new-shape new-shapes updated-shapes] [root-shape new-shapes updated-shapes]
(if-not components-v2
(ctn/make-component-shape group objects file-id components-v2) (ctn/make-component-shape group objects file-id components-v2)
(let [new-id (uuid/next)]
[(assoc group :id new-id)
nil
[(assoc group
:component-id new-id
:component-file file-id
:component-root? true
:main-instance? true)]]))
changes (-> changes changes (-> changes
(pcb/add-component (:id new-shape) (pcb/add-component (:id root-shape)
path path
name name
new-shapes new-shapes
updated-shapes updated-shapes
(:id group) (:id group)
page-id))] page-id))]
[group new-shape changes])) [group (:id root-shape) changes]))
(defn duplicate-component (defn duplicate-component
"Clone the root shape of the component and all children. Generate new "Clone the root shape of the component and all children. Generate new
ids from all of them." ids from all of them."
[component main-instance-page main-instance-shape] [component library-data]
(let [position (gpt/add (gpt/point (:x main-instance-shape) (:y main-instance-shape)) (let [components-v2 (dm/get-in library-data [:options :components-v2])]
(if components-v2
(let [main-instance-page (ctf/get-component-page library-data component)
main-instance-shape (ctf/get-component-root library-data component)
position (gpt/add (gpt/point (:x main-instance-shape) (:y main-instance-shape))
(gpt/point (+ (:width main-instance-shape) 50) 0)) (gpt/point (+ (:width main-instance-shape) 50) 0))
component-root (ctk/get-component-root component) [new-instance-shape new-instance-shapes]
(when (and (some? main-instance-page) (some? main-instance-shape))
(ctn/make-component-instance main-instance-page
component
library-data
position
true))]
[nil nil new-instance-shape new-instance-shapes])
(let [component-root (d/seek #(nil? (:parent-id %)) (vals (:objects component)))
[new-component-shape new-component-shapes _] [new-component-shape new-component-shapes _]
(ctst/clone-object component-root (ctst/clone-object component-root
nil nil
(get component :objects) (get component :objects)
identity) identity)]
[new-component-shape new-component-shapes nil nil]))))
[new-instance-shape new-instance-shapes]
(when (and (some? main-instance-page) (some? main-instance-shape))
(ctn/make-component-instance main-instance-page
{:id (:id new-component-shape)
:name (:name new-component-shape)
:objects (d/index-by :id new-component-shapes)}
(:component-file main-instance-shape)
position))]
[new-component-shape new-component-shapes
new-instance-shape new-instance-shapes]))
(defn generate-instantiate-component (defn generate-instantiate-component
"Generate changes to create a new instance from a component." "Generate changes to create a new instance from a component."
[it file-id component-id position page libraries] [it file-id component-id position page libraries]
(let [component (cph/get-component libraries file-id component-id) (let [component (ctf/get-component libraries file-id component-id)
library (get libraries file-id)
components-v2 (dm/get-in library [:data :options :components-v2])
[new-shape new-shapes] [new-shape new-shapes]
(ctn/make-component-instance page component file-id position) (ctn/make-component-instance page
component
(:data library)
position
components-v2)
changes (reduce #(pcb/add-object %1 %2 {:ignore-touched true}) changes (reduce #(pcb/add-object %1 %2 {:ignore-touched true})
(pcb/empty-changes it (:id page)) (pcb/empty-changes it (:id page))
@ -447,33 +471,32 @@
;; but it's not touched. ;; but it's not touched.
(defn generate-sync-shape-direct (defn generate-sync-shape-direct
"Generate changes to synchronize one shape that the root of a component "Generate changes to synchronize one shape that is the root of a component
instance, and all its children, from the given component." instance, and all its children, from the given component."
[changes libraries container shape-id reset? components-v2] [changes libraries container shape-id reset? components-v2]
(log/debug :msg "Sync shape direct" :shape (str shape-id) :reset? reset?) (log/debug :msg "Sync shape direct" :shape (str shape-id) :reset? reset?)
(let [shape-inst (ctn/get-shape container shape-id) (let [shape-inst (ctn/get-shape container shape-id)]
component (cph/get-component libraries (if (ctk/in-component-instance? shape-inst)
(:component-file shape-inst) (let [library (dm/get-in libraries [(:component-file shape-inst) :data])
(:component-id shape-inst)) component (or (ctkl/get-component library (:component-id shape-inst))
component (or component
(and reset? (and reset?
(ctf/get-deleted-component (ctf/get-deleted-component library (:component-id shape-inst))))
(get-in libraries [(:component-file shape-inst) :data])
(:component-id shape-inst))))
shape-main (when component shape-main (when component
(ctn/get-shape component (:shape-ref shape-inst))) (ctf/get-ref-shape library component shape-inst))
initial-root? (:component-root? shape-inst) initial-root? (:component-root? shape-inst)
root-inst shape-inst root-inst shape-inst
root-main (when component root-main (when component
(ctk/get-component-root component))] (ctf/get-component-root library component))]
(if component (if component
(generate-sync-shape-direct-recursive changes (generate-sync-shape-direct-recursive changes
container container
shape-inst shape-inst
component component
library
shape-main shape-main
root-inst root-inst
root-main root-main
@ -484,10 +507,11 @@
; deleted or the library unlinked, do nothing in v2 or detach in v1. ; deleted or the library unlinked, do nothing in v2 or detach in v1.
(if components-v2 (if components-v2
changes changes
(generate-detach-instance changes container shape-id))))) (generate-detach-instance changes container shape-id))))
changes)))
(defn- generate-sync-shape-direct-recursive (defn- generate-sync-shape-direct-recursive
[changes container shape-inst component shape-main root-inst root-main reset? initial-root? components-v2] [changes container shape-inst component library shape-main root-inst root-main reset? initial-root? components-v2]
(log/debug :msg "Sync shape direct recursive" (log/debug :msg "Sync shape direct recursive"
:shape (str (:name shape-inst)) :shape (str (:name shape-inst))
:component (:name component)) :component (:name component))
@ -522,9 +546,11 @@
set-remote-synced? set-remote-synced?
(change-remote-synced shape-inst container true)) (change-remote-synced shape-inst container true))
component-container (ctf/get-component-container library component)
children-inst (mapv #(ctn/get-shape container %) children-inst (mapv #(ctn/get-shape container %)
(:shapes shape-inst)) (:shapes shape-inst))
children-main (mapv #(ctn/get-shape component %) children-main (mapv #(ctn/get-shape component-container %)
(:shapes shape-main)) (:shapes shape-main))
only-inst (fn [changes child-inst] only-inst (fn [changes child-inst]
@ -545,7 +571,7 @@
child-main child-main
(d/index-of children-main (d/index-of children-main
child-main) child-main)
component component-container
container container
root-inst root-inst
root-main root-main
@ -558,6 +584,7 @@
container container
child-inst child-inst
component component
library
child-main child-main
root-inst root-inst
root-main root-main
@ -589,21 +616,21 @@
[changes libraries container shape-id] [changes libraries container shape-id]
(log/debug :msg "Sync shape inverse" :shape (str shape-id)) (log/debug :msg "Sync shape inverse" :shape (str shape-id))
(let [shape-inst (ctn/get-shape container shape-id) (let [shape-inst (ctn/get-shape container shape-id)
component (cph/get-component libraries library (dm/get-in libraries [(:component-file shape-inst) :data])
(:component-file shape-inst) component (ctkl/get-component library (:component-id shape-inst))
(:component-id shape-inst)) shape-main (ctf/get-ref-shape library component shape-inst)
shape-main (ctn/get-shape component (:shape-ref shape-inst))
initial-root? (:component-root? shape-inst) initial-root? (:component-root? shape-inst)
root-inst shape-inst root-inst shape-inst
root-main (ctk/get-component-root component)] root-main (ctf/get-component-root library component)]
(if component (if component
(generate-sync-shape-inverse-recursive changes (generate-sync-shape-inverse-recursive changes
container container
shape-inst shape-inst
component component
library
shape-main shape-main
root-inst root-inst
root-main root-main
@ -611,7 +638,7 @@
changes))) changes)))
(defn- generate-sync-shape-inverse-recursive (defn- generate-sync-shape-inverse-recursive
[changes container shape-inst component shape-main root-inst root-main initial-root?] [changes container shape-inst component library shape-main root-inst root-main initial-root?]
(log/trace :msg "Sync shape inverse recursive" (log/trace :msg "Sync shape inverse recursive"
:shape (str (:name shape-inst)) :shape (str (:name shape-inst))
:component (:name component)) :component (:name component))
@ -619,7 +646,7 @@
(if (nil? shape-main) (if (nil? shape-main)
;; This should not occur, but protect against it in any case ;; This should not occur, but protect against it in any case
changes changes
(let [component-container (cph/make-container component :component) (let [component-container (ctf/get-component-container library component)
omit-touched? false omit-touched? false
set-remote-synced? (not initial-root?) set-remote-synced? (not initial-root?)
@ -650,7 +677,7 @@
children-inst (mapv #(ctn/get-shape container %) children-inst (mapv #(ctn/get-shape container %)
(:shapes shape-inst)) (:shapes shape-inst))
children-main (mapv #(ctn/get-shape component %) children-main (mapv #(ctn/get-shape component-container %)
(:shapes shape-main)) (:shapes shape-main))
only-inst (fn [changes child-inst] only-inst (fn [changes child-inst]
@ -659,6 +686,7 @@
(d/index-of children-inst (d/index-of children-inst
child-inst) child-inst)
component component
component-container
container container
root-inst root-inst
root-main)) root-main))
@ -674,6 +702,7 @@
container container
child-inst child-inst
component component
library
child-main child-main
root-inst root-inst
root-main root-main
@ -763,9 +792,9 @@
(moved-cb child-inst' child-main))))))))))) (moved-cb child-inst' child-main)))))))))))
(defn- add-shape-to-instance (defn- add-shape-to-instance
[changes component-shape index component container root-instance root-main omit-touched? set-remote-synced?] [changes component-shape index component-page container root-instance root-main omit-touched? set-remote-synced?]
(log/info :msg (str "ADD [P] " (:name component-shape))) (log/info :msg (str "ADD [P] " (:name component-shape)))
(let [component-parent-shape (ctn/get-shape component (:parent-id component-shape)) (let [component-parent-shape (ctn/get-shape component-page (:parent-id component-shape))
parent-shape (d/seek #(ctk/is-main-of? component-parent-shape %) parent-shape (d/seek #(ctk/is-main-of? component-parent-shape %)
(cph/get-children-with-self (:objects container) (cph/get-children-with-self (:objects container)
(:id root-instance))) (:id root-instance)))
@ -793,7 +822,7 @@
[_ new-shapes _] [_ new-shapes _]
(ctst/clone-object component-shape (ctst/clone-object component-shape
(:id parent-shape) (:id parent-shape)
(get component :objects) (get component-page :objects)
update-new-shape update-new-shape
update-original-shape) update-original-shape)
@ -831,14 +860,14 @@
changes'))) changes')))
(defn- add-shape-to-main (defn- add-shape-to-main
[changes shape index component page root-instance root-main] [changes shape index component component-container page root-instance root-main]
(log/info :msg (str "ADD [C] " (:name shape))) (log/info :msg (str "ADD [C] " (:name shape)))
(let [parent-shape (ctn/get-shape page (:parent-id shape)) (let [parent-shape (ctn/get-shape page (:parent-id shape))
component-parent-shape (d/seek #(ctk/is-main-of? % parent-shape) component-parent-shape (d/seek #(ctk/is-main-of? % parent-shape)
(cph/get-children-with-self (:objects component) (cph/get-children-with-self (:objects component-container)
(:id root-main))) (:id root-main)))
all-parents (into [(:id component-parent-shape)] all-parents (into [(:id component-parent-shape)]
(cph/get-parent-ids (:objects component) (cph/get-parent-ids (:objects component-container)
(:id component-parent-shape))) (:id component-parent-shape)))
update-new-shape (fn [new-shape _original-shape] update-new-shape (fn [new-shape _original-shape]
@ -861,13 +890,17 @@
add-obj-change (fn [changes shape'] add-obj-change (fn [changes shape']
(update changes :redo-changes conj (update changes :redo-changes conj
(cond-> (make-change
component-container
{:type :add-obj {:type :add-obj
:id (:id shape') :id (:id shape')
:component-id (:id component)
:parent-id (:parent-id shape') :parent-id (:parent-id shape')
:index index :index index
:ignore-touched true :ignore-touched true
:obj shape'})) :obj shape'})
(ctn/page? component-container)
(assoc :frame-id (:frame-id shape')))))
mod-obj-change (fn [changes shape'] mod-obj-change (fn [changes shape']
(update changes :redo-changes conj (update changes :redo-changes conj

View file

@ -14,6 +14,7 @@
[app.common.pages.helpers :as cph] [app.common.pages.helpers :as cph]
[app.common.spec :as us] [app.common.spec :as us]
[app.common.types.component :as ctk] [app.common.types.component :as ctk]
[app.common.types.container :as ctn]
[app.common.types.page :as ctp] [app.common.types.page :as ctp]
[app.common.types.shape :as cts] [app.common.types.shape :as cts]
[app.common.types.shape-tree :as ctst] [app.common.types.shape-tree :as ctst]
@ -171,7 +172,7 @@
;; not the root. In this case, they must not be deleted, ;; not the root. In this case, they must not be deleted,
;; but hidden (to be able to recover them more easily). ;; but hidden (to be able to recover them more easily).
(let [shape (get objects shape-id) (let [shape (get objects shape-id)
component-shape (cph/get-component-shape objects shape)] component-shape (ctn/get-component-shape objects shape)]
(and (ctk/in-component-instance? shape) (and (ctk/in-component-instance? shape)
(not= shape component-shape) (not= shape component-shape)
(not (ctk/main-instance? component-shape))))) (not (ctk/main-instance? component-shape)))))

View file

@ -208,7 +208,10 @@
data (:workspace-data state)] data (:workspace-data state)]
(-> file (-> file
(dissoc :data) (dissoc :data)
(assoc :pages (:pages data))))) (assoc :options (:options data)
:components (:components data)
:pages (:pages data)
:pages-index (:pages-index data)))))
st/state =)) st/state =))
(def workspace-data (def workspace-data

View file

@ -310,16 +310,16 @@
update-fn #(update %1 %2 gsh/transform-shape (ctm/move-modifiers vector))] update-fn #(update %1 %2 gsh/transform-shape (ctm/move-modifiers vector))]
(reduce update-fn objects children-ids)))) (reduce update-fn objects children-ids))))
root-shape (get objects root-shape-id) root-shape' (get objects root-shape-id)
width (* (:width root-shape) zoom) width (* (:width root-shape') zoom)
height (* (:height root-shape) zoom) height (* (:height root-shape') zoom)
vbox (format-viewbox {:width (:width root-shape 0) vbox (format-viewbox {:width (:width root-shape' 0)
:height (:height root-shape 0)}) :height (:height root-shape' 0)})
root-shape-wrapper root-shape-wrapper
(mf/use-memo (mf/use-memo
(mf/deps objects root-shape) (mf/deps objects root-shape')
(fn [] (fn []
(case (:type root-shape) (case (:type root-shape')
:group (group-wrapper-factory objects) :group (group-wrapper-factory objects)
:frame (frame-wrapper-factory objects))))] :frame (frame-wrapper-factory objects))))]
@ -332,9 +332,9 @@
:xmlns:penpot (when include-metadata? "https://penpot.app/xmlns") :xmlns:penpot (when include-metadata? "https://penpot.app/xmlns")
:fill "none"} :fill "none"}
[:> shape-container {:shape root-shape} [:> shape-container {:shape root-shape'}
[:& (mf/provider muc/is-component?) {:value true} [:& (mf/provider muc/is-component?) {:value true}
[:& root-shape-wrapper {:shape root-shape :view-box vbox}]]]])) [:& root-shape-wrapper {:shape root-shape' :view-box vbox}]]]]))
(mf/defc object-svg (mf/defc object-svg
{::mf/wrap [mf/memo]} {::mf/wrap [mf/memo]}

View file

@ -243,7 +243,9 @@
(events/unlistenByKey key1)))) (events/unlistenByKey key1))))
(mf/use-effect on-resize) (mf/use-effect on-resize)
[:div.dashboard-content {:on-click #(st/emit! (dd/clear-selected-files)) :ref container}
[:div.dashboard-content {:on-click #(st/emit! (dd/clear-selected-files))
:ref container}
(case section (case section
:dashboard-projects :dashboard-projects
[:* [:*

View file

@ -105,12 +105,13 @@
[:span.num-assets (str "\u00A0(") (:count components) ")"]] ;; Unicode 00A0 is non-breaking space [:span.num-assets (str "\u00A0(") (:count components) ")"]] ;; Unicode 00A0 is non-breaking space
[:div.asset-list [:div.asset-list
(for [component (:sample components)] (for [component (:sample components)]
(let [root-id (or (:main-instance-id component) (:id component))] ;; Check for components-v2 in library
[:div.asset-list-item {:key (str "assets-component-" (:id component))} [:div.asset-list-item {:key (str "assets-component-" (:id component))}
[:& component-svg {:group (get-in component [:objects (:id component)]) [:& component-svg {:root-shape (get-in component [:objects root-id])
:objects (:objects component)}] :objects (:objects component)}] ;; Components in the summary come loaded with objects, even in v2
[:div.name-block [:div.name-block
[:span.item-name {:title (:name component)} [:span.item-name {:title (:name component)}
(:name component)]]]) (:name component)]]]))
(when (> (:count components) (count (:sample components))) (when (> (:count components) (count (:sample components)))
[:div.asset-list-item [:div.asset-list-item
[:div.name-block [:div.name-block

View file

@ -12,7 +12,7 @@
[app.common.pages.helpers :as cph] [app.common.pages.helpers :as cph]
[app.common.spec :as us] [app.common.spec :as us]
[app.common.text :as txt] [app.common.text :as txt]
[app.common.types.component :as ctk] [app.common.types.file :as ctf]
[app.config :as cf] [app.config :as cf]
[app.main.data.events :as ev] [app.main.data.events :as ev]
[app.main.data.modal :as modal] [app.main.data.modal :as modal]
@ -365,13 +365,17 @@
;;---- Components box ---- ;;---- Components box ----
(mf/defc components-item (mf/defc components-item
[{:keys [component renaming listing-thumbs? selected-components [{:keys [component renaming listing-thumbs? selected-components file
on-asset-click on-context-menu on-drag-start do-rename cancel-rename on-asset-click on-context-menu on-drag-start do-rename cancel-rename
selected-components-full selected-components-paths]}] selected-components-full selected-components-paths]}]
(let [item-ref (mf/use-ref) (let [item-ref (mf/use-ref)
dragging? (mf/use-state false) dragging? (mf/use-state false)
workspace-read-only? (mf/use-ctx ctx/workspace-read-only?) workspace-read-only? (mf/use-ctx ctx/workspace-read-only?)
components-v2 (mf/use-ctx ctx/components-v2)
file (or (:data file) file)
unselect-all unselect-all
(mf/use-fn (mf/use-fn
(fn [] (fn []
@ -440,8 +444,10 @@
:on-drag-over on-drag-over :on-drag-over on-drag-over
:on-drop on-drop} :on-drop on-drop}
[:& component-svg {:root-shape (ctk/get-component-root component) [:& component-svg {:root-shape (ctf/get-component-root file component)
:objects (:objects component)}] :objects (:objects (if components-v2
(ctf/get-component-page file component)
component))}]
(let [renaming? (= renaming (:id component))] (let [renaming? (= renaming (:id component))]
[:* [:*
[:& editable-label [:& editable-label
@ -460,7 +466,7 @@
[:div.dragging])])])) [:div.dragging])])]))
(mf/defc components-group (mf/defc components-group
[{:keys [file-id prefix groups open-groups renaming listing-thumbs? selected-components on-asset-click [{:keys [file prefix groups open-groups renaming listing-thumbs? selected-components on-asset-click
on-drag-start do-rename cancel-rename on-rename-group on-group on-ungroup on-context-menu on-drag-start do-rename cancel-rename on-rename-group on-group on-ungroup on-context-menu
selected-components-full]}] selected-components-full]}]
(let [group-open? (get open-groups prefix true) (let [group-open? (get open-groups prefix true)
@ -495,7 +501,7 @@
:on-drag-leave on-drag-leave :on-drag-leave on-drag-leave
:on-drag-over on-drag-over :on-drag-over on-drag-over
:on-drop on-drop} :on-drop on-drop}
[:& asset-group-title {:file-id file-id [:& asset-group-title {:file-id (:id file)
:box :components :box :components
:path prefix :path prefix
:group-open? group-open? :group-open? group-open?
@ -528,6 +534,7 @@
:key (:id component) :key (:id component)
:renaming renaming :renaming renaming
:listing-thumbs? listing-thumbs? :listing-thumbs? listing-thumbs?
:file file
:selected-components selected-components :selected-components selected-components
:on-asset-click on-asset-click :on-asset-click on-asset-click
:on-context-menu on-context-menu :on-context-menu on-context-menu
@ -539,7 +546,7 @@
:selected-components-paths selected-components-paths}])]) :selected-components-paths selected-components-paths}])])
(for [[path-item content] groups] (for [[path-item content] groups]
(when-not (empty? path-item) (when-not (empty? path-item)
[:& components-group {:file-id file-id [:& components-group {:file file
:prefix (cph/merge-path-item prefix path-item) :prefix (cph/merge-path-item prefix path-item)
:groups content :groups content
:open-groups open-groups :open-groups open-groups
@ -556,7 +563,7 @@
:selected-components-full selected-components-full}]))])])) :selected-components-full selected-components-full}]))])]))
(mf/defc components-box (mf/defc components-box
[{:keys [file-id local? components listing-thumbs? open? reverse-sort? open-groups selected-assets [{:keys [file local? components listing-thumbs? open? reverse-sort? open-groups selected-assets
on-asset-click on-assets-delete on-clear-selection] :as props}] on-asset-click on-assets-delete on-clear-selection] :as props}]
(let [input-ref (mf/use-ref nil) (let [input-ref (mf/use-ref nil)
state (mf/use-state {:renaming nil state (mf/use-state {:renaming nil
@ -579,14 +586,14 @@
add-component add-component
(mf/use-fn (mf/use-fn
(fn [] (fn []
#(st/emit! (dwl/set-assets-box-open file-id :components true)) #(st/emit! (dwl/set-assets-box-open (:id file) :components true))
(dom/click (mf/ref-val input-ref)))) (dom/click (mf/ref-val input-ref))))
on-file-selected on-file-selected
(mf/use-fn (mf/use-fn
(mf/deps file-id) (mf/deps file)
(fn [blobs] (fn [blobs]
(let [params {:file-id file-id (let [params {:file-id (:id file)
:blobs (seq blobs)}] :blobs (seq blobs)}]
(st/emit! (dwm/upload-media-components params) (st/emit! (dwm/upload-media-components params)
(ptk/event ::ev/event {::ev/name "add-asset-to-library" (ptk/event ::ev/event {::ev/name "add-asset-to-library"
@ -598,22 +605,22 @@
(fn [] (fn []
(let [undo-id (js/Symbol)] (let [undo-id (js/Symbol)]
(if (empty? selected-components) (if (empty? selected-components)
(st/emit! (dwl/duplicate-component {:id (:component-id @state)})) (st/emit! (dwl/duplicate-component (:id file) (:component-id @state)))
(do (do
(st/emit! (dwu/start-undo-transaction undo-id)) (st/emit! (dwu/start-undo-transaction undo-id))
(apply st/emit! (map #(dwl/duplicate-component {:id %}) selected-components)) (apply st/emit! (map (partial dwl/duplicate-component (:id file)) selected-components))
(st/emit! (dwu/commit-undo-transaction undo-id))))))) (st/emit! (dwu/commit-undo-transaction undo-id)))))))
on-delete on-delete
(mf/use-fn (mf/use-fn
(mf/deps @state file-id multi-components? multi-assets?) (mf/deps @state file multi-components? multi-assets?)
(fn [] (fn []
(let [undo-id (js/Symbol)] (let [undo-id (js/Symbol)]
(if (or multi-components? multi-assets?) (if (or multi-components? multi-assets?)
(on-assets-delete) (on-assets-delete)
(st/emit! (dwu/start-undo-transaction undo-id) (st/emit! (dwu/start-undo-transaction undo-id)
(dwl/delete-component {:id (:component-id @state)}) (dwl/delete-component {:id (:component-id @state)})
(dwl/sync-file file-id file-id :components (:component-id @state)) (dwl/sync-file (:id file) (:id file) :components (:component-id @state))
(dwu/commit-undo-transaction undo-id)))))) (dwu/commit-undo-transaction undo-id))))))
on-rename on-rename
@ -716,7 +723,7 @@
on-drag-start on-drag-start
(mf/use-fn (mf/use-fn
(fn [component event] (fn [component event]
(dnd/set-data! event "penpot/component" {:file-id file-id (dnd/set-data! event "penpot/component" {:file-id (:id file)
:component component}) :component component})
(dnd/set-allowed-effect! event "move"))) (dnd/set-allowed-effect! event "move")))
@ -734,7 +741,7 @@
(when (and main-instance-id main-instance-page) ;; Only when :components-v2 is enabled (when (and main-instance-id main-instance-page) ;; Only when :components-v2 is enabled
(st/emit! (dw/go-to-main-instance main-instance-page main-instance-id))))))] (st/emit! (dw/go-to-main-instance main-instance-page main-instance-id))))))]
[:& asset-section {:file-id file-id [:& asset-section {:file-id (:id file)
:title (tr "workspace.assets.components") :title (tr "workspace.assets.components")
:box :components :box :components
:assets-count (count components) :assets-count (count components)
@ -750,7 +757,7 @@
:on-selected on-file-selected}]])]) :on-selected on-file-selected}]])])
[:& asset-section-block {:role :content} [:& asset-section-block {:role :content}
[:& components-group {:file-id file-id [:& components-group {:file file
:prefix "" :prefix ""
:groups groups :groups groups
:open-groups open-groups :open-groups open-groups
@ -2158,7 +2165,7 @@
i/listing-thumbs)]] i/listing-thumbs)]]
(when show-components? (when show-components?
[:& components-box {:file-id (:id file) [:& components-box {:file file
:local? local? :local? local?
:components components :components components
:listing-thumbs? listing-thumbs? :listing-thumbs? listing-thumbs?