0
Fork 0
mirror of https://github.com/penpot/penpot.git synced 2025-04-10 14:01:29 -05:00

🔧 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}]
(binding [pmap/*load-fn* (partial load-pointer conn id)]
{:components (assets-sample (:components data) 4)
:media (assets-sample (:media data) 3)
:colors (assets-sample (:colors data) 3)
:typographies (assets-sample (:typographies data) 3)}))]
(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)
:colors (assets-sample (:colors data) 3)
:typographies (assets-sample (:typographies data) 3)})))]
(->> (db/exec! conn [sql:team-shared-files team-id])
(into #{} (comp
@ -552,7 +555,10 @@
(map (fn [{:keys [id] :as row}]
(binding [pmap/*load-fn* (partial load-pointer conn id)]
(-> 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)))))
(vec)))

View file

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

View file

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

View file

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

View file

@ -270,25 +270,6 @@
[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
"Get the root shape linked to a component for this shape, if any."
[objects shape]

View file

@ -34,6 +34,7 @@
(and (= shape-id (:main-instance-id component))
(= page-id (:main-instance-page component))))
;; Obsolete in components-v2
(defn get-component-root
[component]
(get-in component [:objects (:id component)]))
@ -45,12 +46,12 @@
(= (:component-file shape) library-id)))
(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]
(some? (:shape-ref shape)))
(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."
[shape]
(and (some? (:shape-ref shape))

View file

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

View file

@ -11,6 +11,7 @@
[app.common.geom.shapes :as gsh]
[app.common.pages.common :as common]
[app.common.spec :as us]
[app.common.types.pages-list :as ctpl]
[app.common.types.shape-tree :as ctst]
[clojure.spec.alpha :as s]))
@ -62,6 +63,17 @@
[container 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
"Clone the shape and all children. Generate new ids and detach
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]))
(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
coordinates in the given position."
([container component component-file-id position]
(make-component-instance container component component-file-id position {}))
([container component library-data position components-v2]
(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}}]
(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))
delta (gpt/subtract position orig-pos)
@ -147,20 +166,20 @@
(vswap! frame-ids-map assoc (:id original-shape) (:id new-shape)))
(cond-> new-shape
true
:always
(-> (gsh/move delta)
(dissoc :touched))
(dissoc :touched :main-instance?))
(nil? (:shape-ref original-shape))
(assoc :shape-ref (:id original-shape))
(nil? (:parent-id original-shape))
(assoc :component-id (:id original-shape)
:component-file component-file-id
(assoc :component-id (:id component)
:component-file (:id library-data)
:component-root? true
: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)
(some? (:parent-id original-shape))
@ -169,7 +188,7 @@
[new-shape new-shapes _]
(ctst/clone-object component-shape
nil
(get component :objects)
(if components-v2 (:objects component-page) (:objects component))
update-new-shape
(fn [object _] object)
force-id)
@ -177,10 +196,10 @@
;; If frame-id points to a shape inside the component, remap it to the
;; corresponding new frame shape. If not, set it to the destination frame.
;; Also fix empty parent-id.
remap-frame-id (fn [shape]
(as-> shape $
(update $ :frame-id #(get @frame-ids-map % frame-id))
(update $ :parent-id #(or % (:frame-id $)))))]
remap-frame-id (fn [shape]
(as-> shape $
(update $ :frame-id #(get @frame-ids-map % frame-id))
(update $ :parent-id #(or % (:frame-id $)))))]
[new-shape (map remap-frame-id new-shapes)])))

View file

@ -6,27 +6,27 @@
(ns app.common.types.file
(:require
[app.common.data :as d]
[app.common.data.macros :as dm]
[app.common.files.features :as ffeat]
[app.common.geom.point :as gpt]
[app.common.geom.shapes :as gsh]
[app.common.pages.common :refer [file-version]]
[app.common.pages.helpers :as cph]
[app.common.types.color :as ctc]
[app.common.types.colors-list :as ctcl]
[app.common.types.component :as ctk]
[app.common.types.components-list :as ctkl]
[app.common.types.container :as ctn]
[app.common.types.file.media-object :as ctfm]
[app.common.types.page :as ctp]
[app.common.types.pages-list :as ctpl]
[app.common.types.shape-tree :as ctst]
[app.common.types.typographies-list :as ctyl]
[app.common.types.typography :as cty]
[app.common.uuid :as uuid]
[clojure.spec.alpha :as s]
[cuerdas.core :as str]))
[app.common.data :as d]
[app.common.data.macros :as dm]
[app.common.files.features :as ffeat]
[app.common.geom.point :as gpt]
[app.common.geom.shapes :as gsh]
[app.common.pages.common :refer [file-version]]
[app.common.pages.helpers :as cph]
[app.common.types.color :as ctc]
[app.common.types.colors-list :as ctcl]
[app.common.types.component :as ctk]
[app.common.types.components-list :as ctkl]
[app.common.types.container :as ctn]
[app.common.types.file.media-object :as ctfm]
[app.common.types.page :as ctp]
[app.common.types.pages-list :as ctpl]
[app.common.types.shape-tree :as ctst]
[app.common.types.typographies-list :as ctyl]
[app.common.types.typography :as cty]
[app.common.uuid :as uuid]
[clojure.spec.alpha :as s]
[cuerdas.core :as str]))
;; Specs
@ -116,6 +116,75 @@
([libraries library-id 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
"Delete a component and store it to be able to be recovered later.
@ -123,26 +192,29 @@
([file-data component-id]
(delete-component file-data component-id false))
([file-data component-id skip-undelete?]
(let [components-v2 (dm/get-in file-data [:options :components-v2])
([file-data component-id _skip-undelete?]
(let [_components-v2 (dm/get-in file-data [:options :components-v2])
add-to-deleted-components
(fn [file-data]
(let [component (ctkl/get-component file-data component-id)]
(if (some? component)
(let [page (ctpl/get-page file-data (:main-instance-page component))
main-instance (ctn/get-shape page (:main-instance-id component))
component (assoc component
:main-instance-x (:x main-instance) ; An instance root is always a group,
:main-instance-y (:y main-instance))] ; so it will have :x and :y
(when (nil? main-instance)
(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)))]
;; TODO: replace :deleted-components with a :deleted? flag in normal shapes
;; see task https://tree.taiga.io/project/penpot/task/4998
;;
;; add-to-deleted-components
;; (fn [file-data]
;; (let [component (ctkl/get-component file-data component-id)]
;; (if (some? component)
;; (let [main-instance (get-component-root file-data component)
;; component (assoc component
;; :main-instance-x (:x main-instance) ; An instance root is always a group,
;; :main-instance-y (:y main-instance))] ; or a frame, so it will have :x and :y
;; (when (nil? main-instance)
;; (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
(and components-v2 (not skip-undelete?))
(add-to-deleted-components)
;; (and components-v2 (not skip-undelete?))
;; (add-to-deleted-components)
:always
(ctkl/delete-component component-id)))))
@ -243,8 +315,9 @@
[(ctpl/add-page file-data library-page) (:id library-page) (gpt/point 0 0)]))))
(defn migrate-to-components-v2
"If there is any component in the file library, add a new 'Library backup' and generate
main instances for all components there. Mark the file with the :components-v2 option."
"If there is any component in the file library, add a new 'Library backup', generate
main instances for all components there and remove shapes from library components.
Mark the file with the :components-v2 option."
[file-data]
(let [components (ctkl/components-seq file-data)]
(if (or (empty? components)
@ -262,8 +335,9 @@
[new-shape new-shapes]
(ctn/make-component-instance page
component
(:id file-data)
file-data
position
false
{:main-instance? true})
add-shapes
@ -281,9 +355,10 @@
update-component
(fn [component]
(assoc component
:main-instance-id (:id new-shape)
:main-instance-page page-id))]
(-> component
(assoc :main-instance-id (:id new-shape)
:main-instance-page page-id)
(dissoc :objects)))]
(-> file-data
(ctpl/update-page page-id add-shapes)
@ -292,9 +367,9 @@
add-instance-grid
(fn [file-data components]
(let [position-seq (ctst/generate-shape-grid
(map ctk/get-component-root components)
start-pos
grid-gap)]
(map (partial get-component-root file-data) components)
start-pos
grid-gap)]
(loop [file-data file-data
components-seq (seq components)
position-seq position-seq]
@ -311,7 +386,7 @@
(assoc-in [:options :components-v2] true))))))
(defn- absorb-components
[file-data used-components]
[file-data used-components library-data]
(let [grid-gap 50
; Search for the library page. If not exists, create it.
@ -326,10 +401,17 @@
[main-instance-shape main-instance-shapes]
(ctn/make-component-instance page
component
(:id file-data)
library-data
position
(dm/get-in file-data [:options :components-v2])
{: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-main-instance-shapes
(fn [page]
@ -353,7 +435,7 @@
:path (:path component)
:main-instance-id (:id main-instance-shape)
: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
remap-instances
@ -378,9 +460,9 @@
add-component-grid
(fn [data used-components]
(let [position-seq (ctst/generate-shape-grid
(map #(ctk/get-component-root (first %)) used-components)
start-pos
grid-gap)]
(map #(get-component-root library-data (first %)) used-components)
start-pos
grid-gap)]
(loop [data data
components-seq (seq used-components)
position-seq position-seq]
@ -410,9 +492,9 @@
remap-shape))
%
shapes)))]
(as-> file-data $
(ctcl/add-color $ color)
(reduce remap-shapes $ usages))))]
(as-> file-data $
(ctcl/add-color $ color)
(reduce remap-shapes $ usages))))]
(reduce absorb-color
file-data
@ -434,9 +516,9 @@
remap-shape))
%
shapes)))]
(as-> file-data $
(ctyl/add-typography $ typography)
(reduce remap-shapes $ usages))))]
(as-> file-data $
(ctyl/add-typography $ typography)
(reduce remap-shapes $ usages))))]
(reduce absorb-typography
file-data
@ -452,7 +534,7 @@
(cond-> file-data
(d/not-empty? used-components)
(absorb-components used-components)
(absorb-components used-components library-data)
(d/not-empty? used-colors)
(absorb-colors used-colors)
@ -477,69 +559,82 @@
root (d/seek #(nil? (:parent-id %)) (vals objects))]
(letfn [(show-shape [shape-id level objects]
(let [shape (get objects shape-id)]
(println (str/pad (str (str/repeat " " level)
(:name shape)
(when (seq (:touched shape)) "*")
(when show-ids (str/format " <%s>" (:id shape))))
{:length 20
:type :right})
(show-component shape objects))
(when show-touched
(when (seq (:touched shape))
(println (str (str/repeat " " level)
" "
(str (:touched shape)))))
(when (:remote-synced? shape)
(println (str (str/repeat " " level)
" (remote-synced)"))))
(when (:shapes shape)
(dorun (for [shape-id (:shapes shape)]
(show-shape shape-id (inc level) objects))))))
(let [shape (get objects shape-id)]
(println (str/pad (str (str/repeat " " level)
(when (:main-instance? shape) "{")
(:name shape)
(when (:main-instance? shape) "}")
(when (seq (:touched shape)) "*")
(when show-ids (str/format " <%s>" (:id shape))))
{:length 20
:type :right})
(show-component-info shape objects))
(when show-touched
(when (seq (:touched shape))
(println (str (str/repeat " " level)
" "
(str (:touched shape)))))
(when (:remote-synced? shape)
(println (str (str/repeat " " level)
" (remote-synced)"))))
(when (:shapes shape)
(dorun (for [shape-id (:shapes shape)]
(show-shape shape-id (inc level) objects))))))
(show-component [shape objects]
(if (nil? (:shape-ref shape))
""
(let [root-shape (cph/get-component-shape objects shape)
component-id (when root-shape (:component-id 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 (when component-id
(if component-file
(dm/get-in component-file [:data :components component-id])
(get components component-id)))
component-shape (when (and component (:shape-ref shape))
(dm/get-in component [:objects (:shape-ref shape)]))]
(str/format " %s--> %s%s%s"
(cond (:component-root? shape) "#"
(:component-id shape) "@"
:else "-")
(when component-file (str/format "<%s> " (:name component-file)))
(or (:name component-shape) "?")
(if (or (:component-root? shape)
(nil? (:component-id shape))
true)
""
(let [component-id (:component-id shape)
component-file-id (:component-file shape)
component-file (when component-file-id (get libraries component-file-id nil))
component (if component-file
(dm/get-in component-file [:data :components component-id])
(get components component-id))]
(str/format " (%s%s)"
(when component-file (str/format "<%s> " (:name component-file)))
(:name component))))))))]
(show-component-info [shape objects]
(if (nil? (:shape-ref shape))
(if (:component-root? shape) " #" "")
(let [root-shape (ctn/get-component-shape objects shape)
component-id (when root-shape (:component-id 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 (when component-id
(if component-file
(dm/get-in component-file [:data :components component-id])
(get components component-id)))
component-shape (when component
(if component-file
(get-ref-shape (:data component-file) component shape)
(get-ref-shape file-data component shape)))]
(println "[Page]")
(str/format " %s--> %s%s%s"
(cond (:component-root? shape) "#"
(:component-id shape) "@"
:else "-")
(when component-file (str/format "<%s> " (:name component-file)))
(or (:name component-shape) "?")
(if (or (:component-root? shape)
(nil? (:component-id shape))
true)
""
(let [component-id (:component-id shape)
component-file-id (:component-file shape)
component-file (when component-file-id (get libraries component-file-id nil))
component (if component-file
(dm/get-in component-file [:data :components component-id])
(get components component-id))]
(str/format " (%s%s)"
(when component-file (str/format "<%s> " (:name component-file)))
(:name component))))))))
(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)
(dorun (for [component (vals components)]
(do
(println)
(println (str/format "[%s]" (:name component))
(when show-ids
(str/format " (main: %s/%s)"
(:main-instance-page component)
(:main-instance-id component))))
(show-shape (:id component) 0 (:objects component)))))))))
(println (str/format "[%s]" (:name component)))
(when (:objects component)
(show-shape (:id component) 0 (:objects component)))
(when (:main-instance-page component)
(show-component-instance component)))))))))

View file

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

View file

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

View file

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

View file

@ -233,6 +233,19 @@
(->> (filter (comp t/pointer? val) data)
(resolve-pointers id)
(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/map libraries-fetched))))))))
@ -808,8 +821,8 @@
(not (:component-root? shape)))
parent (get objects parent-id)
component-shape (cph/get-component-shape objects shape)
component-shape-parent (cph/get-component-shape objects parent)
component-shape (ctn/get-component-shape objects shape)
component-shape-parent (ctn/get-component-shape objects parent)
detach? (and instance-part? (not= (:id component-shape)
(:id component-shape-parent)))

View file

@ -17,6 +17,7 @@
[app.common.pages.helpers :as cph]
[app.common.spec :as us]
[app.common.types.color :as ctc]
[app.common.types.component :as ctk]
[app.common.types.components-list :as ctkl]
[app.common.types.container :as ctn]
[app.common.types.file :as ctf]
@ -346,8 +347,9 @@
ptk/WatchEvent
(watch [it state _]
(when (and (some? new-name) (not= "" new-name))
(let [data (get state :workspace-data)
[path name] (cph/parse-path-name new-name)
(let [data (get state :workspace-data)
[path name] (cph/parse-path-name new-name)
components-v2 (features/active-feature? state :components-v2)
update-fn
(fn [component]
@ -355,12 +357,15 @@
;; because there are small possibilities of race
;; conditions with component deletion.
(when component
(-> component
(assoc :path path)
(assoc :name name)
(update :objects
(cond-> component
:always
(assoc :path path
:name name)
(not components-v2)
(update :objects
;; Give the same name to the root shape
#(assoc-in % [id :name] name)))))
#(assoc-in % [id :name] name)))))
changes (-> (pcb/empty-changes it)
(pcb/with-library-data data)
@ -370,30 +375,33 @@
(defn duplicate-component
"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/WatchEvent
(watch [it 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)
components-v2 (features/active-feature? state :components-v2)
main-instance-page (when components-v2
(wsh/lookup-page state (:main-instance-page component)))
main-instance-shape (when components-v2
(ctn/get-shape main-instance-page (:main-instance-id component)))
(ctf/get-component-page (:data library) 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]
(dwlh/duplicate-component component main-instance-page main-instance-shape)
(dwlh/duplicate-component new-component (:data library))
changes (-> (pcb/empty-changes it nil)
(pcb/with-page main-instance-page)
(pcb/with-objects (:objects main-instance-page))
(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)
new-name
new-component-shapes
@ -429,17 +437,22 @@
(ptk/reify ::restore-component
ptk/WatchEvent
(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)
component (ctf/get-deleted-component file-data component-id)
page (ctpl/get-page file-data (:main-instance-page component))
; Make a new main instance, with the same id of the original
components-v2
(features/active-feature? state :components-v2)
; Make a new main instance, with the same id of the original
[_main-instance shapes]
(ctn/make-component-instance page
component
(:id file-data)
file-data
(gpt/point (:main-instance-x component)
(:main-instance-y component))
components-v2
{:main-instance? true
:force-id (:main-instance-id component)})
@ -600,53 +613,53 @@
ptk/WatchEvent
(watch [it state _]
(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)
container (cph/get-container local-file :page page-id)
shape (ctn/get-shape container id)]
local-file (wsh/get-local-file state)
libraries (wsh/get-libraries state)
(when (ctk/in-component-instance? shape)
(let [libraries (wsh/get-libraries state)
container (cph/get-container local-file :page page-id)
shape (ctn/get-shape container id)
changes
(-> (pcb/empty-changes it)
(pcb/with-container container)
(dwlh/generate-sync-shape-inverse libraries container id))
changes
(-> (pcb/empty-changes it)
(pcb/with-container container)
(dwlh/generate-sync-shape-inverse libraries container id))
file-id (:component-file shape)
file (wsh/get-file state file-id)
file-id (:component-file shape)
file (wsh/get-file state file-id)
xf-filter (comp
(filter :local-change?)
(map #(dissoc % :local-change?)))
xf-filter (comp
(filter :local-change?)
(map #(dissoc % :local-change?)))
local-changes (-> changes
(update :redo-changes #(into [] xf-filter %))
(update :undo-changes #(into [] xf-filter %)))
local-changes (-> changes
(update :redo-changes #(into [] xf-filter %))
(update :undo-changes #(into [] xf-filter %)))
xf-remove (comp
(remove :local-change?)
(map #(dissoc % :local-change?)))
xf-remove (comp
(remove :local-change?)
(map #(dissoc % :local-change?)))
nonlocal-changes (-> changes
(update :redo-changes #(into [] xf-remove %))
(update :undo-changes #(into [] xf-remove %)))]
nonlocal-changes (-> changes
(update :redo-changes #(into [] xf-remove %))
(update :undo-changes #(into [] xf-remove %)))]
(log/debug :msg "UPDATE-COMPONENT finished"
:js/local-changes (log-changes
(:redo-changes local-changes)
file)
:js/nonlocal-changes (log-changes
(:redo-changes nonlocal-changes)
file))
(log/debug :msg "UPDATE-COMPONENT finished"
:js/local-changes (log-changes
(:redo-changes local-changes)
file)
:js/nonlocal-changes (log-changes
(:redo-changes nonlocal-changes)
file))
(rx/of
(when (seq (:redo-changes local-changes))
(dch/commit-changes (assoc local-changes
:file-id (:id local-file))))
(when (seq (:redo-changes nonlocal-changes))
(dch/commit-changes (assoc nonlocal-changes
:file-id file-id))))))))
(rx/of
(when (seq (:redo-changes local-changes))
(dch/commit-changes (assoc local-changes
:file-id (:id local-file))))
(when (seq (:redo-changes nonlocal-changes))
(dch/commit-changes (assoc nonlocal-changes
:file-id file-id))))))))))
(defn update-component-sync
[shape-id file-id]

View file

@ -7,6 +7,7 @@
(ns app.main.data.workspace.libraries-helpers
(:require
[app.common.data :as d]
[app.common.data.macros :as dm]
[app.common.geom.point :as gpt]
[app.common.geom.shapes :as gsh]
[app.common.logging :as log]
@ -17,10 +18,12 @@
[app.common.text :as txt]
[app.common.types.color :as ctc]
[app.common.types.component :as ctk]
[app.common.types.components-list :as ctkl]
[app.common.types.container :as ctn]
[app.common.types.file :as ctf]
[app.common.types.shape-tree :as ctst]
[app.common.types.typography :as cty]
[app.common.uuid :as uuid]
[app.main.data.workspace.groups :as dwg]
[app.main.data.workspace.state-helpers :as wsh]
[cljs.spec.alpha :as s]
@ -84,54 +87,75 @@
name (:name group)
[path name] (cph/parse-path-name name)
[new-shape new-shapes updated-shapes]
(ctn/make-component-shape group objects file-id components-v2)
[root-shape new-shapes updated-shapes]
(if-not 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
(pcb/add-component (:id new-shape)
(pcb/add-component (:id root-shape)
path
name
new-shapes
updated-shapes
(:id group)
page-id))]
[group new-shape changes]))
page-id))]
[group (:id root-shape) changes]))
(defn duplicate-component
"Clone the root shape of the component and all children. Generate new
ids from all of them."
[component main-instance-page main-instance-shape]
(let [position (gpt/add (gpt/point (:x main-instance-shape) (:y main-instance-shape))
(gpt/point (+ (:width main-instance-shape) 50) 0))
[component library-data]
(let [components-v2 (dm/get-in library-data [:options :components-v2])]
(if components-v2
component-root (ctk/get-component-root component)
(let [main-instance-page (ctf/get-component-page library-data component)
main-instance-shape (ctf/get-component-root library-data component)
[new-component-shape new-component-shapes _]
(ctst/clone-object component-root
nil
(get component :objects)
identity)
position (gpt/add (gpt/point (:x main-instance-shape) (:y main-instance-shape))
(gpt/point (+ (:width main-instance-shape) 50) 0))
[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])
[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))]
(let [component-root (d/seek #(nil? (:parent-id %)) (vals (:objects component)))
[new-component-shape new-component-shapes
new-instance-shape new-instance-shapes]))
[new-component-shape new-component-shapes _]
(ctst/clone-object component-root
nil
(get component :objects)
identity)]
[new-component-shape new-component-shapes nil nil]))))
(defn generate-instantiate-component
"Generate changes to create a new instance from a component."
[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]
(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})
(pcb/empty-changes it (:id page))
@ -447,47 +471,47 @@
;; but it's not touched.
(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."
[changes libraries container shape-id reset? components-v2]
(log/debug :msg "Sync shape direct" :shape (str shape-id) :reset? reset?)
(let [shape-inst (ctn/get-shape container shape-id)
component (cph/get-component libraries
(:component-file shape-inst)
(:component-id shape-inst))
component (or component
(and reset?
(ctf/get-deleted-component
(get-in libraries [(:component-file shape-inst) :data])
(:component-id shape-inst))))
shape-main (when component
(ctn/get-shape component (:shape-ref shape-inst)))
(let [shape-inst (ctn/get-shape container shape-id)]
(if (ctk/in-component-instance? shape-inst)
(let [library (dm/get-in libraries [(:component-file shape-inst) :data])
component (or (ctkl/get-component library (:component-id shape-inst))
(and reset?
(ctf/get-deleted-component library (:component-id shape-inst))))
initial-root? (:component-root? shape-inst)
shape-main (when component
(ctf/get-ref-shape library component shape-inst))
root-inst shape-inst
root-main (when component
(ctk/get-component-root component))]
initial-root? (:component-root? shape-inst)
(if component
(generate-sync-shape-direct-recursive changes
container
shape-inst
component
shape-main
root-inst
root-main
reset?
initial-root?
components-v2)
root-inst shape-inst
root-main (when component
(ctf/get-component-root library component))]
(if component
(generate-sync-shape-direct-recursive changes
container
shape-inst
component
library
shape-main
root-inst
root-main
reset?
initial-root?
components-v2)
; If the component is not found, because the master component has been
; deleted or the library unlinked, do nothing in v2 or detach in v1.
(if components-v2
changes
(generate-detach-instance changes container shape-id)))))
(if components-v2
changes
(generate-detach-instance changes container shape-id))))
changes)))
(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"
:shape (str (:name shape-inst))
:component (:name component))
@ -522,20 +546,22 @@
set-remote-synced?
(change-remote-synced shape-inst container true))
children-inst (mapv #(ctn/get-shape container %)
(:shapes shape-inst))
children-main (mapv #(ctn/get-shape component %)
(:shapes shape-main))
component-container (ctf/get-component-container library component)
children-inst (mapv #(ctn/get-shape container %)
(:shapes shape-inst))
children-main (mapv #(ctn/get-shape component-container %)
(:shapes shape-main))
only-inst (fn [changes child-inst]
(if-not (and omit-touched?
(contains? (:touched shape-inst)
:shapes-group))
(remove-shape changes
child-inst
container
omit-touched?)
changes))
(contains? (:touched shape-inst)
:shapes-group))
(remove-shape changes
child-inst
container
omit-touched?)
changes))
only-main (fn [changes child-main]
(if-not (and omit-touched?
@ -545,7 +571,7 @@
child-main
(d/index-of children-main
child-main)
component
component-container
container
root-inst
root-main
@ -558,6 +584,7 @@
container
child-inst
component
library
child-main
root-inst
root-main
@ -567,12 +594,12 @@
moved (fn [changes child-inst child-main]
(move-shape
changes
child-inst
(d/index-of children-inst child-inst)
(d/index-of children-main child-main)
container
omit-touched?))]
changes
child-inst
(d/index-of children-inst child-inst)
(d/index-of children-main child-main)
container
omit-touched?))]
(compare-children changes
children-inst
@ -588,22 +615,22 @@
the values in the shape and all its children."
[changes libraries container shape-id]
(log/debug :msg "Sync shape inverse" :shape (str shape-id))
(let [shape-inst (ctn/get-shape container shape-id)
component (cph/get-component libraries
(:component-file shape-inst)
(:component-id shape-inst))
shape-main (ctn/get-shape component (:shape-ref shape-inst))
(let [shape-inst (ctn/get-shape container shape-id)
library (dm/get-in libraries [(:component-file shape-inst) :data])
component (ctkl/get-component library (:component-id shape-inst))
shape-main (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-main (ctk/get-component-root component)]
root-inst shape-inst
root-main (ctf/get-component-root library component)]
(if component
(generate-sync-shape-inverse-recursive changes
container
shape-inst
component
library
shape-main
root-inst
root-main
@ -611,7 +638,7 @@
changes)))
(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"
:shape (str (:name shape-inst))
:component (:name component))
@ -619,7 +646,7 @@
(if (nil? shape-main)
;; This should not occur, but protect against it in any case
changes
(let [component-container (cph/make-container component :component)
(let [component-container (ctf/get-component-container library component)
omit-touched? false
set-remote-synced? (not initial-root?)
@ -650,7 +677,7 @@
children-inst (mapv #(ctn/get-shape container %)
(:shapes shape-inst))
children-main (mapv #(ctn/get-shape component %)
children-main (mapv #(ctn/get-shape component-container %)
(:shapes shape-main))
only-inst (fn [changes child-inst]
@ -659,6 +686,7 @@
(d/index-of children-inst
child-inst)
component
component-container
container
root-inst
root-main))
@ -674,6 +702,7 @@
container
child-inst
component
library
child-main
root-inst
root-main
@ -681,12 +710,12 @@
moved (fn [changes child-inst child-main]
(move-shape
changes
child-main
(d/index-of children-main child-main)
(d/index-of children-inst child-inst)
component-container
false))
changes
child-main
(d/index-of children-main child-main)
(d/index-of children-inst child-inst)
component-container
false))
changes
(compare-children changes
@ -763,9 +792,9 @@
(moved-cb child-inst' child-main)))))))))))
(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)))
(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 %)
(cph/get-children-with-self (:objects container)
(:id root-instance)))
@ -793,7 +822,7 @@
[_ new-shapes _]
(ctst/clone-object component-shape
(:id parent-shape)
(get component :objects)
(get component-page :objects)
update-new-shape
update-original-shape)
@ -831,14 +860,14 @@
changes')))
(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)))
(let [parent-shape (ctn/get-shape page (:parent-id 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)))
all-parents (into [(:id component-parent-shape)]
(cph/get-parent-ids (:objects component)
(cph/get-parent-ids (:objects component-container)
(:id component-parent-shape)))
update-new-shape (fn [new-shape _original-shape]
@ -854,20 +883,24 @@
[_new-shape new-shapes updated-shapes]
(ctst/clone-object shape
(:id component-parent-shape)
(get page :objects)
update-new-shape
update-original-shape)
(:id component-parent-shape)
(get page :objects)
update-new-shape
update-original-shape)
add-obj-change (fn [changes shape']
(update changes :redo-changes conj
{:type :add-obj
:id (:id shape')
:component-id (:id component)
:parent-id (:parent-id shape')
:index index
:ignore-touched true
:obj shape'}))
(cond-> (make-change
component-container
{:type :add-obj
:id (:id shape')
:parent-id (:parent-id shape')
:index index
:ignore-touched true
:obj shape'})
(ctn/page? component-container)
(assoc :frame-id (:frame-id shape')))))
mod-obj-change (fn [changes shape']
(update changes :redo-changes conj
@ -899,8 +932,8 @@
changes' (reduce add-obj-change changes new-shapes)
changes' (update changes' :redo-changes conj {:type :reg-objects
:component-id (:id component)
:shapes all-parents})
:component-id (:id component)
:shapes all-parents})
changes' (reduce mod-obj-change changes' updated-shapes)
changes' (reduce del-obj-change changes' new-shapes)]

View file

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

View file

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

View file

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

View file

@ -243,7 +243,9 @@
(events/unlistenByKey key1))))
(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
:dashboard-projects
[:*

View file

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

View file

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