Merge pull request #2967 from penpot/hiru-refactor-instances

🔧 Read component shapes from pages
This commit is contained in:
Pablo Alba 2023-03-28 12:00:10 +02:00 committed by GitHub
commit b73ce14560
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
42 changed files with 3351 additions and 2781 deletions

View file

@ -9,6 +9,7 @@
[app.common.data :as d]
[app.common.file-builder :as fb]
[app.common.media :as cm]
[app.common.types.components-list :as ctkl]
[app.common.uuid :as uuid]
[app.util.dom :as dom]
[app.util.json :as json]
@ -108,7 +109,7 @@
components-stream
(->> files-stream
(rx/flat-map vals)
(rx/filter #(d/not-empty? (get-in % [:data :components])))
(rx/filter #(d/not-empty? (ctkl/components-seq (:data %))))
(rx/flat-map e/parse-library-components))
pages-stream

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)))
@ -1638,7 +1651,7 @@
all-objects (merge (:objects page) paste-objects)
changes (-> (dws/prepare-duplicate-changes all-objects page selected delta it)
changes (-> (dws/prepare-duplicate-changes all-objects page selected delta it nil)
(pcb/amend-changes (partial process-rchange media-idx))
(pcb/amend-changes (partial change-add-obj-index paste-objects selected index)))

View file

@ -34,22 +34,20 @@
(declare commit-changes)
(defn- add-group-id
(defn- add-undo-group
[changes state]
(let [undo (:workspace-undo state)
items (:items undo)
index (or (:index undo) (dec (count items)))
prev-item (when-not (or (empty? items) (= index -1))
(get items index))
group-id (:group-id prev-item)
add-group-id? (and
(not (nil? group-id))
(= (get-in changes [:redo-changes 0 :type]) :mod-obj)
(= (get-in prev-item [:redo-changes 0 :type]) :add-obj)) ;; This is a copy-and-move with mouse+alt
]
(cond-> changes add-group-id? (assoc :group-id group-id))))
(let [undo (:workspace-undo state)
items (:items undo)
index (or (:index undo) (dec (count items)))
prev-item (when-not (or (empty? items) (= index -1))
(get items index))
undo-group (:undo-group prev-item)
add-undo-group? (and
(not (nil? undo-group))
(= (get-in changes [:redo-changes 0 :type]) :mod-obj)
(= (get-in prev-item [:redo-changes 0 :type]) :add-obj))] ;; This is a copy-and-move with mouse+alt
(cond-> changes add-undo-group? (assoc :undo-group undo-group))))
(def commit-changes? (ptk/type? ::commit-changes))
@ -82,7 +80,7 @@
(pcb/set-stack-undo? stack-undo?)
(pcb/with-objects objects))
ids)
changes (add-group-id changes state)]
changes (add-undo-group changes state)]
(rx/concat
(if (seq (:redo-changes changes))
(let [changes (cond-> changes reg-objects? (pcb/resize-parents ids))
@ -165,15 +163,24 @@
changes)))
(defn commit-changes
"Schedules a list of changes to execute now, and add the corresponding undo changes to
the undo stack.
Options:
- save-undo?: if set to false, do not add undo changes.
- undo-group: if some consecutive changes (or even transactions) share the same
undo-group, they will be undone or redone in a single step
"
[{:keys [redo-changes undo-changes
origin save-undo? file-id group-id stack-undo?]
:or {save-undo? true stack-undo? false}}]
origin save-undo? file-id undo-group stack-undo?]
:or {save-undo? true stack-undo? false undo-group (uuid/next)}}]
(log/debug :msg "commit-changes"
:js/undo-group (str undo-group)
:js/redo-changes redo-changes
:js/undo-changes undo-changes)
(let [error (volatile! nil)
(let [error (volatile! nil)
page-id (:current-page-id @st/state)
frames (changed-frames redo-changes (wsh/lookup-page-objects @st/state))]
frames (changed-frames redo-changes (wsh/lookup-page-objects @st/state))]
(ptk/reify ::commit-changes
cljs.core/IDeref
(-deref [_]
@ -184,8 +191,8 @@
:page-id page-id
:frames frames
:save-undo? save-undo?
:stack-undo? stack-undo?
:group-id group-id})
:undo-group undo-group
:stack-undo? stack-undo?})
ptk/UpdateEvent
(update [_ state]
@ -215,7 +222,7 @@
add-page-id
(fn [{:keys [id type page] :as change}]
(cond-> change
(page-change? type)
(and (page-change? type) (nil? (:page-id change)))
(assoc :page-id (or id (:id page)))))
changes-by-pages
@ -234,5 +241,5 @@
(when (and save-undo? (seq undo-changes))
(let [entry {:undo-changes undo-changes
:redo-changes redo-changes
:group-id group-id}]
:undo-group undo-group}]
(rx/of (dwu/append-undo entry stack-undo?)))))))))))

View file

@ -63,16 +63,16 @@
(when-not (or (empty? items) (= index -1))
(let [item (get items index)
changes (:undo-changes item)
group-id (:group-id item)
undo-group (:undo-group item)
find-first-group-idx (fn ffgidx[index]
(let [item (get items index)]
(if (= (:group-id item) group-id)
(if (= (:undo-group item) undo-group)
(ffgidx (dec index))
(inc index))))
undo-group-index (when group-id
undo-group-index (when undo-group
(find-first-group-idx index))]
(if group-id
(if undo-group
(rx/of (undo-to-index (dec undo-group-index)))
(rx/of (dwu/materialize-undo changes (dec index))
(dch/commit-changes {:redo-changes changes
@ -94,16 +94,16 @@
(when-not (or (empty? items) (= index (dec (count items))))
(let [item (get items (inc index))
changes (:redo-changes item)
group-id (:group-id item)
undo-group (:undo-group item)
find-last-group-idx (fn flgidx [index]
(let [item (get items index)]
(if (= (:group-id item) group-id)
(if (= (:undo-group item) undo-group)
(flgidx (inc index))
(dec index))))
redo-group-index (when group-id
redo-group-index (when undo-group
(find-last-group-idx (inc index)))]
(if group-id
(if undo-group
(rx/of (undo-to-index redo-group-index))
(rx/of (dwu/materialize-undo changes (inc index))
(dch/commit-changes {:redo-changes changes

View file

@ -17,11 +17,11 @@
[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]
[app.common.types.file.media-object :as ctfm]
[app.common.types.pages-list :as ctpl]
[app.common.types.typography :as ctt]
[app.common.uuid :as uuid]
[app.main.data.dashboard :as dd]
@ -317,7 +317,7 @@
shapes (dwg/shapes-for-grouping objects selected)]
(when-not (empty? shapes)
(let [[group _ changes]
(dwlh/generate-add-component it shapes objects page-id file-id components-v2)]
(dwlh/generate-add-component it shapes objects page-id file-id components-v2 dwg/prepare-create-group)]
(when-not (empty? (:redo-changes changes))
(rx/of (dch/commit-changes changes)
(dws/select-shapes (d/ordered-set (:id group)))))))))))
@ -346,8 +346,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 +356,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 +374,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
@ -413,12 +420,12 @@
(let [data (get state :workspace-data)]
(if (features/active-feature? state :components-v2)
(let [component (ctkl/get-component data id)
page (ctpl/get-page data (:main-instance-page component))
shape (ctn/get-shape page (:main-instance-id component))]
(rx/of (dwsh/delete-shapes (:id page) #{(:id shape)})))
page (ctf/get-component-page data component)
shape (ctf/get-component-root data component)]
(rx/of (dwsh/delete-shapes (:id page) #{(:id shape)}))) ;; Deleting main root triggers component delete
(let [changes (-> (pcb/empty-changes it)
(pcb/with-library-data data)
(pcb/delete-component id false))]
(pcb/delete-component id))]
(rx/of (dch/commit-changes changes))))))))
(defn restore-component
@ -429,34 +436,18 @@
(ptk/reify ::restore-component
ptk/WatchEvent
(watch [it state _]
(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
[_main-instance shapes]
(ctn/make-component-instance page
component
(:id file-data)
(gpt/point (:main-instance-x component)
(:main-instance-y component))
{:main-instance? true
:force-id (:main-instance-id component)})
changes (-> (pcb/empty-changes it)
(pcb/with-library-data file-data)
(pcb/with-page page))
changes (reduce #(pcb/add-object %1 %2 {:ignore-touched true})
changes
shapes)
; restore-component change needs to be done after add main instance
; because when undo changes, the orden is inverse
changes (pcb/restore-component changes component-id)]
(rx/of (dch/commit-changes (assoc changes :file-id library-id)))))))
(let [library-data (wsh/get-file state library-id)
component (ctkl/get-deleted-component library-data component-id)
page (ctf/get-component-page library-data component)
shapes (cph/get-children-with-self (:objects component) (:main-instance-id component))
changes (-> (pcb/empty-changes it)
(pcb/with-library-data library-data)
(pcb/with-page page))
changes (reduce #(pcb/add-object %1 %2 {:ignore-touched true})
changes
shapes)
changes (pcb/restore-component changes component-id)]
(rx/of (dch/commit-changes changes))))))
(defn instantiate-component
"Create a new shape in the current page, from the component with the given id
@ -471,8 +462,10 @@
(let [page (wsh/lookup-page state)
libraries (wsh/get-libraries state)
changes (pcb/empty-changes it (:id page))
[new-shape changes]
(dwlh/generate-instantiate-component it
(dwlh/generate-instantiate-component changes
file-id
component-id
position
@ -594,76 +587,79 @@
NOTE: It's possible that the component to update is defined in an
external library file, so this function may cause to modify a file
different of that the one we are currently editing."
[id]
(us/assert ::us/uuid id)
(ptk/reify ::update-component
ptk/WatchEvent
(watch [it state _]
(log/info :msg "UPDATE-COMPONENT of shape" :id (str id))
(let [page-id (get state :current-page-id)
([id] (update-component id nil))
([id undo-group]
(us/assert ::us/uuid id)
(ptk/reify ::update-component
ptk/WatchEvent
(watch [it state _]
(log/info :msg "UPDATE-COMPONENT of shape" :id (str id) :undo-group undo-group)
(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/set-undo-group undo-group)
(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]
(ptk/reify ::update-component-sync
ptk/WatchEvent
(watch [_ state _]
(let [current-file-id (:current-file-id state)
page (wsh/lookup-page state)
shape (ctn/get-shape page shape-id)
undo-id (js/Symbol)]
(rx/of
(dwu/start-undo-transaction undo-id)
(update-component shape-id)
(sync-file current-file-id file-id :components (:component-id shape))
(when (not= current-file-id file-id)
(sync-file file-id file-id :components (:component-id shape)))
(dwu/commit-undo-transaction undo-id))))))
([shape-id file-id] (update-component-sync shape-id file-id nil))
([shape-id file-id undo-group]
(ptk/reify ::update-component-sync
ptk/WatchEvent
(watch [_ state _]
(let [current-file-id (:current-file-id state)
page (wsh/lookup-page state)
shape (ctn/get-shape page shape-id)
undo-id (js/Symbol)]
(rx/of
(dwu/start-undo-transaction undo-id)
(update-component shape-id undo-group)
(sync-file current-file-id file-id :components (:component-id shape) undo-group)
(when (not= current-file-id file-id)
(sync-file file-id file-id :components (:component-id shape) undo-group))
(dwu/commit-undo-transaction undo-id)))))))
(defn update-component-in-bulk
[shapes file-id]
@ -673,7 +669,7 @@
(let [undo-id (js/Symbol)]
(rx/concat
(rx/of (dwu/start-undo-transaction undo-id))
(rx/map #(update-component-sync (:id %) file-id) (rx/from shapes))
(rx/map #(update-component-sync (:id %) file-id (uuid/next)) (rx/from shapes))
(rx/of (dwu/commit-undo-transaction undo-id)))))))
(declare sync-file-2nd-stage)
@ -690,6 +686,8 @@
([file-id library-id]
(sync-file file-id library-id nil nil))
([file-id library-id asset-type asset-id]
(sync-file file-id library-id asset-type asset-id nil))
([file-id library-id asset-type asset-id undo-group]
(us/assert ::us/uuid file-id)
(us/assert ::us/uuid library-id)
(us/assert (s/nilable #{:colors :components :typographies}) asset-type)
@ -709,7 +707,8 @@
:file (dwlh/pretty-file file-id state)
:library (dwlh/pretty-file library-id state)
:asset-type asset-type
:asset-id asset-id)
:asset-id asset-id
:undo-group undo-group)
(let [file (wsh/get-file state file-id)
sync-components? (or (nil? asset-type) (= asset-type :components))
@ -718,7 +717,8 @@
library-changes (reduce
pcb/concat-changes
(pcb/empty-changes it)
(-> (pcb/empty-changes it)
(pcb/set-undo-group undo-group))
[(when sync-components?
(dwlh/generate-sync-library it file-id :components asset-id library-id state))
(when sync-colors?
@ -727,7 +727,8 @@
(dwlh/generate-sync-library it file-id :typographies asset-id library-id state))])
file-changes (reduce
pcb/concat-changes
(pcb/empty-changes it)
(-> (pcb/empty-changes it)
(pcb/set-undo-group undo-group))
[(when sync-components?
(dwlh/generate-sync-file it file-id :components asset-id library-id state))
(when sync-colors?
@ -758,7 +759,7 @@
:library-id library-id})))
(when (and (seq (:redo-changes library-changes))
sync-components?)
(rx/of (sync-file-2nd-stage file-id library-id asset-id))))))))))
(rx/of (sync-file-2nd-stage file-id library-id asset-id undo-group))))))))))
(defn- sync-file-2nd-stage
"If some components have been modified, we need to launch another synchronization
@ -769,7 +770,7 @@
;; recursively. But for this not to cause an infinite loop, we need to
;; implement updated-at at component level, to detect what components have
;; not changed, and then not to apply sync and terminate the loop.
[file-id library-id asset-id]
[file-id library-id asset-id undo-group]
(us/assert ::us/uuid file-id)
(us/assert ::us/uuid library-id)
(us/assert (s/nilable ::us/uuid) asset-id)
@ -782,7 +783,8 @@
(let [file (wsh/get-file state file-id)
changes (reduce
pcb/concat-changes
(pcb/empty-changes it)
(-> (pcb/empty-changes it)
(pcb/set-undo-group undo-group))
[(dwlh/generate-sync-file it file-id :components asset-id library-id state)
(dwlh/generate-sync-library it file-id :components asset-id library-id state)])]
@ -856,7 +858,7 @@
check-changes
(fn [[event data]]
(let [{:keys [changes save-undo?]} (deref event)
(let [{:keys [changes save-undo? undo-group]} (deref event)
components-changed (reduce #(into %1 (ch/components-changed data %2))
#{}
changes)]
@ -864,7 +866,7 @@
(log/info :msg "DETECTED COMPONENTS CHANGED"
:ids (map str components-changed))
(run! st/emit!
(map #(update-component-sync % (:id data))
(map #(update-component-sync % (:id data) undo-group)
components-changed)))))]
(when components-v2

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,11 +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.main.data.workspace.groups :as dwg]
[app.common.uuid :as uuid]
[app.main.data.workspace.state-helpers :as wsh]
[cljs.spec.alpha :as s]
[clojure.set :as set]))
@ -63,7 +65,7 @@
"If there is exactly one id, and it's a group or a frame, and not already a component,
use it as root. Otherwise, create a group that contains all ids. Then, make a
component with it, and link all shapes to their corresponding one in the component."
[it shapes objects page-id file-id components-v2]
[it shapes objects page-id file-id components-v2 prepare-create-group]
(let [[group changes]
(if (and (= (count shapes) 1)
(or (= (:type (first shapes)) :group)
@ -74,67 +76,91 @@
(let [group-name (if (= 1 (count shapes))
(:name (first shapes))
"Component 1")]
(dwg/prepare-create-group it
objects
page-id
shapes
group-name
(not (ctk/instance-root? (first shapes))))))
(prepare-create-group it ; This function needs to be passed as argument
objects ; to avoid a circular dependence
page-id
shapes
group-name
(not (ctk/instance-root? (first shapes))))))
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))
component-instance-extra-data (if components-v2 {:main-instance? true} {})
[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-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
component-instance-extra-data))]
[new-component-shape new-component-shapes
new-instance-shape new-instance-shapes]))
[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 _]
(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)
[changes file-id component-id position page libraries]
(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))
changes
new-shapes)]
[new-shape changes]))
@ -217,7 +243,7 @@
(let [file (wsh/get-file state file-id)
components-v2 (get-in file [:options :components-v2])]
(loop [local-components (vals (get file :components))
(loop [local-components (ctkl/components-seq file)
changes (pcb/empty-changes it)]
(if-let [local-component (first local-components)]
(recur (next local-components)
@ -447,47 +473,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?
(ctkl/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 +548,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 +573,7 @@
child-main
(d/index-of children-main
child-main)
component
component-container
container
root-inst
root-main
@ -558,6 +586,7 @@
container
child-inst
component
library
child-main
root-inst
root-main
@ -567,12 +596,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 +617,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 +640,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 +648,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 +679,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 +688,7 @@
(d/index-of children-inst
child-inst)
component
component-container
container
root-inst
root-main))
@ -674,6 +704,7 @@
container
child-inst
component
library
child-main
root-inst
root-main
@ -681,12 +712,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 +794,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 +824,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 +862,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 +885,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 +934,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,12 +14,14 @@
[app.common.pages.changes-builder :as pcb]
[app.common.pages.helpers :as cph]
[app.common.spec :as us]
[app.common.types.component :as ctk]
[app.common.types.page :as ctp]
[app.common.types.shape.interactions :as ctsi]
[app.common.uuid :as uuid]
[app.main.data.modal :as md]
[app.main.data.workspace.changes :as dch]
[app.main.data.workspace.collapse :as dwc]
[app.main.data.workspace.libraries-helpers :as dwlh]
[app.main.data.workspace.state-helpers :as wsh]
[app.main.data.workspace.undo :as dwu]
[app.main.data.workspace.zoom :as dwz]
@ -330,7 +332,7 @@
(defn prepare-duplicate-changes
"Prepare objects to duplicate: generate new id, give them unique names,
move to the desired position, and recalculate parents and frames as needed."
[all-objects page ids delta it]
[all-objects page ids delta it libraries]
(let [shapes (map (d/getf all-objects) ids)
unames (volatile! (cp/retrieve-used-names (:objects page)))
update-unames! (fn [new-name] (vswap! unames conj new-name))
@ -351,7 +353,8 @@
update-unames!
ids-map
%2
delta)
delta
libraries)
init-changes))]
(-> changes
@ -359,11 +362,25 @@
(prepare-duplicate-guides shapes page ids-map delta))))
(defn- prepare-duplicate-shape-change
([changes objects page unames update-unames! ids-map obj delta]
(prepare-duplicate-shape-change changes objects page unames update-unames! ids-map obj delta (:frame-id obj) (:parent-id obj)))
([changes objects page unames update-unames! ids-map obj delta libraries]
(prepare-duplicate-shape-change changes objects page unames update-unames! ids-map obj delta libraries (:frame-id obj) (:parent-id obj)))
([changes objects page unames update-unames! ids-map obj delta frame-id parent-id]
(if (some? obj)
([changes objects page unames update-unames! ids-map obj delta libraries frame-id parent-id]
(cond
(nil? obj)
changes
(ctk/main-instance? obj)
(let [[_new-shape changes]
(dwlh/generate-instantiate-component changes
(:component-file obj)
(:component-id obj)
(gpt/point (:x obj) (:y obj))
page
libraries)]
changes)
:else
(let [frame? (cph/frame-shape? obj)
new-id (ids-map (:id obj))
parent-id (or parent-id frame-id)
@ -392,11 +409,11 @@
ids-map
child
delta
libraries
(if frame? new-id frame-id)
new-id))
changes
(map (d/getf objects) (:shapes obj))))
changes)))
(map (d/getf objects) (:shapes obj)))))))
(defn- prepare-duplicate-flows
[changes shapes page ids-map]
@ -531,7 +548,7 @@
(defn duplicate-selected
([move-delta?]
(duplicate-selected move-delta? false))
([move-delta? add-group-id?]
([move-delta? add-undo-group?]
(ptk/reify ::duplicate-selected
ptk/WatchEvent
(watch [it state _]
@ -545,10 +562,12 @@
(calc-duplicate-delta obj state objects)
(gpt/point 0 0))
changes (->> (prepare-duplicate-changes objects page selected delta it)
libraries (wsh/get-libraries state)
changes (->> (prepare-duplicate-changes objects page selected delta it libraries)
(duplicate-changes-update-indices objects selected))
changes (cond-> changes add-group-id? (assoc :group-id (uuid/random)))
changes (cond-> changes add-undo-group? (assoc :undo-group (uuid/random)))
id-original (first selected)

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)))))
@ -291,7 +292,7 @@
changes (reduce (fn [changes component-id]
;; It's important to delete the component before the main instance, because we
;; need to store the instance position if we want to restore it later.
(pcb/delete-component changes component-id components-v2))
(pcb/delete-component changes component-id))
changes
components-to-delete)

View file

@ -40,10 +40,6 @@
([state page-id]
(dm/get-in state [:workspace-data :pages-index page-id :options])))
(defn lookup-component-objects
([state component-id]
(dm/get-in state [:workspace-data :components component-id :objects])))
(defn lookup-local-components
([state]
(dm/get-in state [:workspace-data :components])))

View file

@ -67,11 +67,11 @@
(add-undo-entry state entry))))
(defn- accumulate-undo-entry
[state {:keys [undo-changes redo-changes group-id]}]
[state {:keys [undo-changes redo-changes undo-group]}]
(-> state
(update-in [:workspace-undo :transaction :undo-changes] #(into undo-changes %))
(update-in [:workspace-undo :transaction :redo-changes] #(into % redo-changes))
(assoc-in [:workspace-undo :transaction :group-id] group-id)))
(assoc-in [:workspace-undo :transaction :undo-group] undo-group)))
(defn append-undo
[entry stack?]
@ -79,29 +79,31 @@
(ptk/reify ::append-undo
ptk/UpdateEvent
(update [_ state]
(cond
(and (get-in state [:workspace-undo :transaction])
(or (not stack?)
(d/not-empty? (get-in state [:workspace-undo :transaction :undo-changes]))
(d/not-empty? (get-in state [:workspace-undo :transaction :redo-changes]))))
(accumulate-undo-entry state entry)
(cond
(and (get-in state [:workspace-undo :transaction])
(or (not stack?)
(d/not-empty? (get-in state [:workspace-undo :transaction :undo-changes]))
(d/not-empty? (get-in state [:workspace-undo :transaction :redo-changes]))))
(accumulate-undo-entry state entry)
stack?
(stack-undo-entry state entry)
stack?
(stack-undo-entry state entry)
:else
(add-undo-entry state entry)))))
:else
(add-undo-entry state entry)))))
(def empty-tx
{:undo-changes [] :redo-changes []})
(defn start-undo-transaction [id]
(defn start-undo-transaction
"Start a transaction, so that every changes inside are added together in a single undo entry."
[id]
(ptk/reify ::start-undo-transaction
ptk/UpdateEvent
(update [_ state]
;; We commit the old transaction before starting the new one
(let [current-tx (get-in state [:workspace-undo :transaction])
pending-tx (get-in state [:workspace-undo :transactions-pending])]
(let [current-tx (get-in state [:workspace-undo :transaction])
pending-tx (get-in state [:workspace-undo :transactions-pending])]
(cond-> state
(nil? current-tx) (assoc-in [:workspace-undo :transaction] empty-tx)
(nil? pending-tx) (assoc-in [:workspace-undo :transactions-pending] #{id})

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

@ -7,6 +7,7 @@
(ns app.main.ui.workspace.libraries
(:require
[app.common.data :as d]
[app.common.types.components-list :as ctkl]
[app.main.data.dashboard :as dd]
[app.main.data.modal :as modal]
[app.main.data.workspace.libraries :as dwl]
@ -44,7 +45,7 @@
(defn local-library-str
[library]
(let [components-count (count (get-in library [:data :components] []))
(let [components-count (count (or (ctkl/components-seq (:data library)) []))
graphics-count (count (get-in library [:data :media] []))
colors-count (count (get-in library [:data :colors] []))
typography-count (count (get-in library [:data :typographies] []))]

View file

@ -12,7 +12,8 @@
[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.components-list :as ctkl]
[app.common.types.file :as ctf]
[app.config :as cf]
[app.main.data.events :as ev]
[app.main.data.modal :as modal]
@ -365,13 +366,20 @@
;;---- 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)
root-shape (ctf/get-component-root file component)
component-container (if components-v2
(ctf/get-component-page file component)
component)
unselect-all
(mf/use-fn
(fn []
@ -440,27 +448,29 @@
:on-drag-over on-drag-over
:on-drop on-drop}
[:& component-svg {:root-shape (ctk/get-component-root component)
:objects (:objects component)}]
(let [renaming? (= renaming (:id component))]
(when (and (some? root-shape) (some? component-container))
[:*
[:& editable-label
{:class-name (dom/classnames
: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)
:editing? renaming?
:disable-dbl-click? true
:on-change do-rename
:on-cancel cancel-rename}]
(when @dragging?
[:div.dragging])])]))
[:& component-svg {:root-shape root-shape
:objects (:objects component-container)}]
(let [renaming? (= renaming (:id component))]
[:*
[:& editable-label
{:class-name (dom/classnames
: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)
:editing? renaming?
:disable-dbl-click? true
:on-change do-rename
:on-cancel cancel-rename}]
(when @dragging?
[: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 +505,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 +538,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 +550,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 +567,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 +590,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 +609,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 +727,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 +745,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 +761,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
@ -1931,8 +1942,8 @@
(l/derived (fn [state]
(let [wfile (:workspace-data state)]
(if (= (:id wfile) id)
(vals (get wfile :components))
(vals (get-in state [:workspace-libraries id :data :components])))))
(ctkl/components-seq wfile)
(ctkl/components-seq (get-in state [:workspace-libraries id :data])))))
st/state =))
(defn file-typography-ref
@ -2158,7 +2169,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?

View file

@ -6,7 +6,7 @@
(ns app.main.ui.workspace.sidebar.options.menus.component
(:require
[app.common.types.components-list :as ctkl]
[app.common.types.components-list :as ctkl]
[app.common.types.file :as ctf]
[app.main.data.modal :as modal]
[app.main.data.workspace :as dw]

View file

@ -11,6 +11,7 @@
[app.common.logging :as l]
[app.common.math :as mth]
[app.common.spec :as us]
[app.common.types.components-list :as ctkl]
[app.common.uri :as u]
[app.main.data.fonts :as df]
[app.main.features :as features]
@ -247,7 +248,7 @@
:border "0px solid black"}]]])]
[:ul.nav
(for [[id data] (get-in file [:data :components])]
(for [[id data] (ctkl/components (:data file))]
(let [on-click (fn [event]
(dom/prevent-default event)
(swap! state assoc :component-id id))]

View file

@ -9,6 +9,7 @@
[app.common.data :as d]
[app.common.media :as cm]
[app.common.text :as ct]
[app.common.types.components-list :as ctkl]
[app.config :as cfg]
[app.main.render :as r]
[app.main.repo :as rp]
@ -51,7 +52,7 @@
:version current-version
:libraries (->> (:libraries file) (into #{}) (mapv str))
:exportType (d/name export-type)
:hasComponents (d/not-empty? (get-in file [:data :components]))
:hasComponents (d/not-empty? (ctkl/components-seq (:data file)))
:hasDeletedComponents (d/not-empty? (get-in file [:data :deleted-components]))
:hasMedia (d/not-empty? (get-in file [:data :media]))
:hasColors (d/not-empty? (get-in file [:data :colors]))
@ -329,7 +330,7 @@
(select-keys (get external-refs (:id file))))
media (-> (get-in file [:data :media])
(select-keys (get external-refs (:id file))))
components (-> (get-in file [:data :components])
components (-> (ctkl/components (:data file))
(select-keys (get external-refs (:id file))))]
(cond-> target
(d/not-empty? colors)
@ -434,7 +435,7 @@
components-stream
(->> files-stream
(rx/flat-map vals)
(rx/filter #(d/not-empty? (get-in % [:data :components])))
(rx/filter #(d/not-empty? (ctkl/components-seq (:data %))))
(rx/flat-map parse-library-components))
deleted-components-stream