mirror of
https://github.com/penpot/penpot.git
synced 2025-04-13 07:21:40 -05:00
Merge pull request #2967 from penpot/hiru-refactor-instances
🔧 Read component shapes from pages
This commit is contained in:
commit
b73ce14560
42 changed files with 3351 additions and 2781 deletions
|
@ -13,6 +13,7 @@
|
|||
[app.common.pages.helpers :as cph]
|
||||
[app.common.pages.migrations :as pmg]
|
||||
[app.common.spec :as us]
|
||||
[app.common.types.components-list :as ctkl]
|
||||
[app.common.types.file :as ctf]
|
||||
[app.common.types.shape-tree :as ctt]
|
||||
[app.config :as cf]
|
||||
|
@ -200,6 +201,16 @@
|
|||
::db/check-deleted? false})]
|
||||
(blob/decode (:content row))))
|
||||
|
||||
(defn load-all-pointers!
|
||||
[data]
|
||||
(doseq [[_id page] (:pages-index data)]
|
||||
(when (pmap/pointer-map? page)
|
||||
(pmap/load! page)))
|
||||
(doseq [[_id component] (:components data)]
|
||||
(when (pmap/pointer-map? component)
|
||||
(pmap/load! component)))
|
||||
data)
|
||||
|
||||
(defn persist-pointers!
|
||||
[conn file-id]
|
||||
(doseq [[id item] @pmap/*tracked*]
|
||||
|
@ -485,17 +496,23 @@
|
|||
(defn get-team-shared-files
|
||||
[conn team-id]
|
||||
(letfn [(assets-sample [assets limit]
|
||||
(let [sorted-assets (->> (vals assets)
|
||||
(sort-by #(str/lower (:name %))))]
|
||||
{:count (count sorted-assets)
|
||||
:sample (into [] (take limit sorted-assets))}))
|
||||
(let [sorted-assets (->> (vals assets)
|
||||
(sort-by #(str/lower (:name %))))]
|
||||
{:count (count sorted-assets)
|
||||
:sample (into [] (take limit sorted-assets))}))
|
||||
|
||||
(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 [load-objects (fn [component]
|
||||
(binding [pmap/*load-fn* (partial load-pointer conn id)]
|
||||
(ctf/load-component-objects data component)))
|
||||
components-sample (-> (assets-sample (ctkl/components data) 4)
|
||||
(update :sample
|
||||
#(map load-objects %)))]
|
||||
{: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 +569,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)))
|
||||
|
||||
|
@ -836,18 +856,23 @@
|
|||
(let [library (db/get-by-id conn :file id)]
|
||||
(when (:is-shared library)
|
||||
(let [ldata (-> library decode-row pmg/migrate-file :data)]
|
||||
(binding [pmap/*load-fn* (partial load-pointer conn id)]
|
||||
(load-all-pointers! ldata))
|
||||
(->> (db/query conn :file-library-rel {:library-file-id id})
|
||||
(map :file-id)
|
||||
(keep #(db/get-by-id conn :file % ::db/check-deleted? false))
|
||||
(map decode-row)
|
||||
(map pmg/migrate-file)
|
||||
(run! (fn [{:keys [id data revn] :as file}]
|
||||
(let [data (ctf/absorb-assets data ldata)]
|
||||
(db/update! conn :file
|
||||
{:revn (inc revn)
|
||||
:data (blob/encode data)
|
||||
:modified-at (dt/now)}
|
||||
{:id id})))))))))
|
||||
(binding [pmap/*tracked* (atom {})
|
||||
pmap/*load-fn* (partial load-pointer conn id)]
|
||||
(let [data (ctf/absorb-assets data ldata)]
|
||||
(db/update! conn :file
|
||||
{:revn (inc revn)
|
||||
:data (blob/encode data)
|
||||
:modified-at (dt/now)}
|
||||
{:id id}))
|
||||
(persist-pointers! conn id)))))))))
|
||||
|
||||
(s/def ::set-file-shared
|
||||
(s/keys :req [::rpc/profile-id]
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
[app.common.data :as d]
|
||||
[app.common.logging :as l]
|
||||
[app.common.pages.migrations :as pmg]
|
||||
[app.common.types.components-list :as ctkl]
|
||||
[app.common.types.file :as ctf]
|
||||
[app.common.types.shape-tree :as ctt]
|
||||
[app.config :as cf]
|
||||
|
@ -204,36 +205,31 @@
|
|||
(filter #(ctf/used-in? file-data library-id % :component))
|
||||
components))
|
||||
|
||||
find-used-components
|
||||
find-unused-components
|
||||
(fn [components files-data]
|
||||
; Find what components are used in any of the files.
|
||||
; Find what components are NOT used in any of the files.
|
||||
(loop [files-data files-data
|
||||
components components
|
||||
used-components #{}]
|
||||
components components]
|
||||
(let [file-data (first files-data)]
|
||||
(if (or (nil? file-data) (empty? components))
|
||||
used-components
|
||||
components
|
||||
(let [used-components-file (find-used-components-file components file-data)]
|
||||
(recur (rest files-data)
|
||||
(into #{} (remove used-components-file) components)
|
||||
(into used-components used-components-file)))))))
|
||||
(into #{} (remove used-components-file) components)))))))
|
||||
|
||||
deleted-components (set (vals (:deleted-components library-data)))
|
||||
saved-components (find-used-components deleted-components
|
||||
(cons library-data
|
||||
(retrieve-client-files conn library-id)))
|
||||
new-deleted-components (d/index-by :id (vec saved-components))
|
||||
|
||||
total (- (count deleted-components)
|
||||
(count saved-components))]
|
||||
deleted-components (set (ctkl/deleted-components-seq library-data))
|
||||
unused-components (find-unused-components deleted-components
|
||||
(cons library-data
|
||||
(retrieve-client-files conn library-id)))
|
||||
total (count unused-components)]
|
||||
|
||||
(when-not (zero? total)
|
||||
(l/debug :hint "clean deleted components" :total total)
|
||||
(let [new-data (-> library-data
|
||||
(assoc :deleted-components new-deleted-components)
|
||||
(blob/encode))]
|
||||
(let [new-data (reduce #(ctkl/delete-component %1 (:id %2))
|
||||
library-data
|
||||
unused-components)]
|
||||
(db/update! conn :file
|
||||
{:data new-data}
|
||||
{:data (blob/encode new-data)}
|
||||
{:id library-id})))))
|
||||
|
||||
(def ^:private sql:get-unused-fragments
|
||||
|
|
|
@ -255,6 +255,11 @@
|
|||
(subvec v 0 index)
|
||||
(subvec v (inc index))))
|
||||
|
||||
(defn without-obj
|
||||
"Clear collection from specified obj and without nil values."
|
||||
[coll o]
|
||||
(into [] (filter #(not= % o)) coll))
|
||||
|
||||
(defn zip [col1 col2]
|
||||
(map vector col1 col2))
|
||||
|
||||
|
@ -477,6 +482,17 @@
|
|||
(->> (apply c/iteration args)
|
||||
(concat-all)))
|
||||
|
||||
(defn insert-at-index
|
||||
"Insert a list of elements at the given index of a previous list.
|
||||
Replace all existing elems."
|
||||
[elems index new-elems]
|
||||
(let [[before after] (split-at index elems)
|
||||
p? (set new-elems)]
|
||||
(concat-vec []
|
||||
(remove p? before)
|
||||
new-elems
|
||||
(remove p? after))))
|
||||
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;; Data Parsing / Conversion
|
||||
|
|
|
@ -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})]
|
||||
|
||||
|
|
|
@ -26,15 +26,6 @@
|
|||
[app.common.types.shape-tree :as ctst]
|
||||
[app.common.types.typographies-list :as ctyl]))
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;; Specific helpers
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
||||
(defn- without-obj
|
||||
"Clear collection from specified obj and without nil values."
|
||||
[coll o]
|
||||
(into [] (filter #(not= % o)) coll))
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;; Page Transformation Changes
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
@ -111,27 +102,9 @@
|
|||
|
||||
(defmethod process-change :del-obj
|
||||
[data {:keys [page-id component-id id ignore-touched]}]
|
||||
(letfn [(delete-from-parent [parent]
|
||||
(let [parent (update parent :shapes without-obj id)]
|
||||
(cond-> parent
|
||||
(and (:shape-ref parent)
|
||||
(not ignore-touched))
|
||||
(-> (update :touched cph/set-touched-group :shapes-group)
|
||||
(dissoc :remote-synced?)))))
|
||||
|
||||
(delete-from-objects [objects]
|
||||
(if-let [target (get objects id)]
|
||||
(let [parent-id (or (:parent-id target)
|
||||
(:frame-id target))
|
||||
children (cph/get-children id objects)]
|
||||
(-> (reduce dissoc objects children)
|
||||
(dissoc id)
|
||||
(d/update-when parent-id delete-from-parent)))
|
||||
objects))]
|
||||
|
||||
(if page-id
|
||||
(d/update-in-when data [:pages-index page-id :objects] delete-from-objects)
|
||||
(d/update-in-when data [:components component-id :objects] delete-from-objects))))
|
||||
(if page-id
|
||||
(d/update-in-when data [:pages-index page-id] ctst/delete-shape id ignore-touched)
|
||||
(d/update-in-when data [:components component-id] ctst/delete-shape id ignore-touched)))
|
||||
|
||||
;; reg-objects operation "regenerates" the geometry and selrect of the parent groups
|
||||
(defmethod process-change :reg-objects
|
||||
|
@ -197,7 +170,7 @@
|
|||
(insert-items [prev-shapes index shapes]
|
||||
(let [prev-shapes (or prev-shapes [])]
|
||||
(if index
|
||||
(cph/insert-at-index prev-shapes index shapes)
|
||||
(d/insert-at-index prev-shapes index shapes)
|
||||
(cph/append-at-the-end prev-shapes shapes))))
|
||||
|
||||
(add-to-parent [parent index shapes]
|
||||
|
@ -226,7 +199,7 @@
|
|||
(not ignore-touched))]
|
||||
|
||||
(-> objects
|
||||
(d/update-in-when [pid :shapes] without-obj sid)
|
||||
(d/update-in-when [pid :shapes] d/without-obj sid)
|
||||
(d/update-in-when [pid :shapes] d/vec-without-nils)
|
||||
(cond-> component? (d/update-when pid #(-> %
|
||||
(update :touched cph/set-touched-group :shapes-group)
|
||||
|
@ -301,7 +274,7 @@
|
|||
|
||||
(defmethod process-change :mov-page
|
||||
[data {:keys [id index]}]
|
||||
(update data :pages cph/insert-at-index index [id]))
|
||||
(update data :pages d/insert-at-index index [id]))
|
||||
|
||||
(defmethod process-change :add-color
|
||||
[data {:keys [color]}]
|
||||
|
|
|
@ -39,6 +39,12 @@
|
|||
[changes stack-undo?]
|
||||
(assoc changes :stack-undo? stack-undo?))
|
||||
|
||||
(defn set-undo-group
|
||||
[changes undo-group]
|
||||
(cond-> changes
|
||||
(some? undo-group)
|
||||
(assoc :undo-group undo-group)))
|
||||
|
||||
(defn with-page
|
||||
[changes page]
|
||||
(vary-meta changes assoc
|
||||
|
@ -80,7 +86,8 @@
|
|||
[changes1 changes2]
|
||||
{:redo-changes (d/concat-vec (:redo-changes changes1) (:redo-changes changes2))
|
||||
:undo-changes (d/concat-vec (:undo-changes changes1) (:undo-changes changes2))
|
||||
:origin (:origin changes1)})
|
||||
:origin (:origin changes1)
|
||||
:undo-group (:undo-group changes1)})
|
||||
|
||||
; TODO: remove this when not needed
|
||||
(defn- assert-page-id
|
||||
|
@ -598,19 +605,21 @@
|
|||
(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]
|
||||
(-> undo-changes
|
||||
(d/preconj {:type :del-component
|
||||
:id id})
|
||||
:id id
|
||||
:skip-undelete? true})
|
||||
(into (comp (map :id)
|
||||
(map lookupf)
|
||||
(map mk-change))
|
||||
|
@ -629,7 +638,7 @@
|
|||
:id id
|
||||
:name (:name new-component)
|
||||
:path (:path new-component)
|
||||
:objects (:objects new-component)})
|
||||
:objects (:objects new-component)}) ;; this won't exist in components-v2
|
||||
(update :undo-changes d/preconj {:type :mod-component
|
||||
:id id
|
||||
:name (:name prev-component)
|
||||
|
@ -638,28 +647,13 @@
|
|||
changes)))
|
||||
|
||||
(defn delete-component
|
||||
[changes id components-v2]
|
||||
[changes id]
|
||||
(assert-library changes)
|
||||
(let [library-data (::library-data (meta changes))
|
||||
prev-component (get-in library-data [:components id])]
|
||||
(-> changes
|
||||
(update :redo-changes conj {:type :del-component
|
||||
:id id})
|
||||
(update :undo-changes
|
||||
(fn [undo-changes]
|
||||
(cond-> undo-changes
|
||||
components-v2
|
||||
(d/preconj {:type :purge-component
|
||||
:id id})
|
||||
|
||||
: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))})))))))
|
||||
(-> changes
|
||||
(update :redo-changes conj {:type :del-component
|
||||
:id id})
|
||||
(update :undo-changes d/preconj {:type :restore-component
|
||||
:id id})))
|
||||
|
||||
(defn restore-component
|
||||
[changes id]
|
||||
|
|
|
@ -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]
|
||||
|
|
|
@ -9,6 +9,8 @@
|
|||
[app.common.data :as d]
|
||||
[app.common.data.macros :as dm]
|
||||
[app.common.spec :as us]
|
||||
[app.common.types.components-list :as ctkl]
|
||||
[app.common.types.pages-list :as ctpl]
|
||||
[app.common.types.shape.layout :as ctl]
|
||||
[app.common.uuid :as uuid]
|
||||
[cuerdas.core :as str]))
|
||||
|
@ -270,25 +272,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]
|
||||
|
@ -319,8 +302,8 @@
|
|||
(us/assert uuid? id)
|
||||
|
||||
(-> (if (= type :page)
|
||||
(get-in file [:pages-index id])
|
||||
(get-in file [:components id]))
|
||||
(ctpl/get-page file id)
|
||||
(ctkl/get-component file id))
|
||||
(assoc :type type)))
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
@ -340,15 +323,6 @@
|
|||
(update page :objects
|
||||
#(into % (d/index-by :id objects-list))))
|
||||
|
||||
(defn insert-at-index
|
||||
[objects index ids]
|
||||
(let [[before after] (split-at index objects)
|
||||
p? (set ids)]
|
||||
(d/concat-vec []
|
||||
(remove p? before)
|
||||
ids
|
||||
(remove p? after))))
|
||||
|
||||
(defn append-at-the-end
|
||||
[prev-ids ids]
|
||||
(reduce (fn [acc id]
|
||||
|
|
|
@ -36,7 +36,9 @@
|
|||
|
||||
(defn get-component-root
|
||||
[component]
|
||||
(get-in component [:objects (:id component)]))
|
||||
(if (some? (:main-instance-id component))
|
||||
(get-in component [:objects (:main-instance-id component)])
|
||||
(get-in component [:objects (:id component)])))
|
||||
|
||||
(defn uses-library-components?
|
||||
"Check if the shape uses any component in the given library."
|
||||
|
@ -45,12 +47,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))
|
||||
|
@ -66,5 +68,3 @@
|
|||
:remote-synced?
|
||||
:shape-ref
|
||||
:touched))
|
||||
|
||||
|
||||
|
|
|
@ -10,9 +10,18 @@
|
|||
[app.common.data.macros :as dm]
|
||||
[app.common.files.features :as feat]))
|
||||
|
||||
(defn components
|
||||
[file-data]
|
||||
(d/removem (fn [[_ component]] (:deleted component))
|
||||
(:components file-data)))
|
||||
|
||||
(defn components-seq
|
||||
[file-data]
|
||||
(vals (:components file-data)))
|
||||
(remove :deleted (vals (:components file-data))))
|
||||
|
||||
(defn deleted-components-seq
|
||||
[file-data]
|
||||
(filter :deleted (vals (:components file-data))))
|
||||
|
||||
(defn add-component
|
||||
[file-data {:keys [id name path main-instance-id main-instance-page shapes]}]
|
||||
|
@ -23,11 +32,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
|
||||
|
@ -51,7 +62,15 @@
|
|||
|
||||
(defn get-component
|
||||
[file-data component-id]
|
||||
(get-in file-data [:components component-id]))
|
||||
(let [component (get-in file-data [:components component-id])]
|
||||
(when-not (:deleted component)
|
||||
component)))
|
||||
|
||||
(defn get-deleted-component
|
||||
[file-data component-id]
|
||||
(let [component (get-in file-data [:components component-id])]
|
||||
(when (:deleted component)
|
||||
component)))
|
||||
|
||||
(defn update-component
|
||||
[file-data component-id f]
|
||||
|
@ -61,3 +80,10 @@
|
|||
[file-data component-id]
|
||||
(update file-data :components dissoc component-id))
|
||||
|
||||
(defn mark-component-deleted
|
||||
[file-data component-id]
|
||||
(assoc-in file-data [:components component-id :deleted] true))
|
||||
|
||||
(defn mark-component-undeleted
|
||||
[file-data component-id]
|
||||
(d/dissoc-in file-data [:components component-id :deleted]))
|
||||
|
|
|
@ -6,11 +6,12 @@
|
|||
|
||||
(ns app.common.types.container
|
||||
(:require
|
||||
[app.common.data.macros :as dm]
|
||||
[app.common.geom.point :as gpt]
|
||||
[app.common.geom.shapes :as gsh]
|
||||
[app.common.pages.common :as common]
|
||||
[app.common.spec :as us]
|
||||
[app.common.types.components-list :as ctkl]
|
||||
[app.common.types.pages-list :as ctpl]
|
||||
[app.common.types.shape-tree :as ctst]
|
||||
[clojure.spec.alpha :as s]))
|
||||
|
||||
|
@ -42,8 +43,8 @@
|
|||
(us/assert uuid? id)
|
||||
|
||||
(-> (if (= type :page)
|
||||
(dm/get-in file [:pages-index id])
|
||||
(dm/get-in file [:components id]))
|
||||
(ctpl/get-page file id)
|
||||
(ctkl/get-component file id))
|
||||
(assoc :type type)))
|
||||
|
||||
(defn get-shape
|
||||
|
@ -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))
|
||||
(and (not 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)])))
|
||||
|
||||
|
|
|
@ -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,56 +116,100 @@
|
|||
([libraries library-id component-id]
|
||||
(ctkl/get-component (dm/get-in libraries [library-id :data]) component-id)))
|
||||
|
||||
(defn delete-component
|
||||
"Delete a component and store it to be able to be recovered later.
|
||||
(defn get-component-library
|
||||
"Retrieve the library the component belongs to."
|
||||
[libraries instance-root]
|
||||
(get libraries (:component-file instance-root)))
|
||||
|
||||
Remember also the position of the main instance."
|
||||
(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 (and components-v2 (not (:deleted component)))
|
||||
(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 (and components-v2 (not (:deleted component)))
|
||||
(-> file-data
|
||||
(get-component-page component)
|
||||
(ctn/get-shape (:main-instance-id component)))
|
||||
(ctk/get-component-root component))))
|
||||
|
||||
(defn get-component-shape
|
||||
"Retrieve one shape in the component by id."
|
||||
[file-data component shape-id]
|
||||
(let [components-v2 (dm/get-in file-data [:options :components-v2])]
|
||||
(if (and components-v2 (not (:deleted component)))
|
||||
(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 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 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 (and components-v2 component (nil? (:objects component))) ;; This operation may be called twice, e.g. in an idempotent change
|
||||
(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
|
||||
"Mark a component as deleted and store the main instance shapes iside it, to
|
||||
be able to be recovered later."
|
||||
([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])
|
||||
|
||||
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)))]
|
||||
|
||||
(cond-> file-data
|
||||
(and components-v2 (not skip-undelete?))
|
||||
(add-to-deleted-components)
|
||||
|
||||
:always
|
||||
(ctkl/delete-component component-id)))))
|
||||
|
||||
(defn get-deleted-component
|
||||
"Retrieve a component that has been deleted but still is in the safe store."
|
||||
[file-data component-id]
|
||||
(dm/get-in file-data [:deleted-components component-id]))
|
||||
(let [components-v2 (dm/get-in file-data [:options :components-v2])]
|
||||
(if (or (not components-v2) skip-undelete?)
|
||||
(ctkl/delete-component file-data component-id)
|
||||
(-> file-data
|
||||
(ctkl/update-component component-id (partial load-component-objects file-data))
|
||||
(ctkl/mark-component-deleted component-id))))))
|
||||
|
||||
(defn restore-component
|
||||
"Recover a deleted component and put it again in place."
|
||||
"Recover a deleted component and all its shapes and put all this again in place."
|
||||
[file-data component-id]
|
||||
(let [component (-> (dm/get-in file-data [:deleted-components component-id])
|
||||
(dissoc :main-instance-x :main-instance-y))]
|
||||
(cond-> file-data
|
||||
(some? component)
|
||||
(-> (assoc-in [:components component-id] component)
|
||||
(d/dissoc-in [:deleted-components component-id])))))
|
||||
(-> file-data
|
||||
(ctkl/update-component component-id #(dissoc % :objects))
|
||||
(ctkl/mark-component-undeleted component-id)))
|
||||
|
||||
(defn purge-component
|
||||
"Remove permanently a component."
|
||||
[file-data component-id]
|
||||
(d/dissoc-in file-data [:deleted-components component-id]))
|
||||
(ctkl/delete-component file-data component-id))
|
||||
|
||||
(defmulti uses-asset?
|
||||
"Checks if a shape uses the given asset."
|
||||
|
@ -243,8 +287,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 +307,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 +327,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 +339,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 +358,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 +373,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 +407,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 +432,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 +464,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 +488,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 +506,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)
|
||||
|
@ -473,73 +527,86 @@
|
|||
([file-data page-id libraries show-ids show-touched]
|
||||
(let [page (ctpl/get-page file-data page-id)
|
||||
objects (:objects page)
|
||||
components (:components file-data)
|
||||
components (ctkl/components file-data)
|
||||
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
|
||||
(ctkl/get-component (:data component-file) 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
|
||||
(ctkl/get-component (:data component-file) 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)))))))))
|
||||
|
||||
|
|
|
@ -709,7 +709,7 @@
|
|||
(update shape :shapes
|
||||
(fn [shapes]
|
||||
(if (vector? shapes)
|
||||
(cph/insert-at-index shapes index value)
|
||||
(d/insert-at-index shapes index value)
|
||||
(d/concat-vec shapes value))))
|
||||
(update shape :shapes d/concat-vec value)))
|
||||
|
||||
|
|
|
@ -6,8 +6,8 @@
|
|||
|
||||
(ns app.common.types.pages-list
|
||||
(:require
|
||||
[app.common.data.macros :as dm]
|
||||
[app.common.pages.helpers :as cph]))
|
||||
[app.common.data :as d]
|
||||
[app.common.data.macros :as dm]))
|
||||
|
||||
(defn get-page
|
||||
[file-data id]
|
||||
|
@ -23,7 +23,7 @@
|
|||
(cond
|
||||
exists? pages
|
||||
(nil? index) (conj pages id)
|
||||
:else (cph/insert-at-index pages index [id])))))
|
||||
:else (d/insert-at-index pages index [id])))))
|
||||
(update :pages-index assoc id (dissoc page :index))))
|
||||
|
||||
(defn pages-seq
|
||||
|
|
|
@ -36,7 +36,7 @@
|
|||
(conj shapes id)
|
||||
|
||||
:else
|
||||
(cph/insert-at-index shapes index [id]))))
|
||||
(d/insert-at-index shapes index [id]))))
|
||||
|
||||
update-parent
|
||||
(fn [parent]
|
||||
|
@ -49,28 +49,63 @@
|
|||
(-> (update :touched cph/set-touched-group :shapes-group)
|
||||
(dissoc :remote-synced?)))))
|
||||
|
||||
;; TODO: this looks wrong, why we allow nil values?
|
||||
update-objects
|
||||
(fn [objects parent-id]
|
||||
(if (and (or (nil? parent-id) (contains? objects parent-id))
|
||||
(or (nil? frame-id) (contains? objects frame-id)))
|
||||
(let [parent-id (if (contains? objects parent-id)
|
||||
parent-id
|
||||
uuid/zero)
|
||||
frame-id (if (contains? objects frame-id)
|
||||
frame-id
|
||||
uuid/zero)]
|
||||
(-> objects
|
||||
(assoc id (-> shape
|
||||
(assoc :frame-id frame-id)
|
||||
(assoc :parent-id parent-id)
|
||||
(assoc :id id)))
|
||||
(update parent-id update-parent))
|
||||
objects))
|
||||
(update parent-id update-parent))))
|
||||
|
||||
parent-id (or parent-id frame-id)]
|
||||
|
||||
(update container :objects update-objects parent-id)))
|
||||
|
||||
(defn get-shape
|
||||
"Get a shape identified by id"
|
||||
[container id]
|
||||
(-> container :objects (get id)))
|
||||
|
||||
(defn set-shape
|
||||
"Replace a shape in the tree with a new one"
|
||||
[container shape]
|
||||
(assoc-in container [:objects (:id shape)] shape))
|
||||
|
||||
(defn delete-shape
|
||||
"Remove a shape and all its children from the tree.
|
||||
|
||||
Remove it also from its parent, and marks it as touched
|
||||
if needed, unless ignore-touched is true."
|
||||
([container shape-id]
|
||||
(delete-shape container shape-id false))
|
||||
|
||||
([container shape-id ignore-touched]
|
||||
(letfn [(delete-from-parent [parent]
|
||||
(let [parent (update parent :shapes d/without-obj shape-id)]
|
||||
(cond-> parent
|
||||
(and (:shape-ref parent) (not ignore-touched))
|
||||
(-> (update :touched cph/set-touched-group :shapes-group)
|
||||
(dissoc :remote-synced?)))))
|
||||
|
||||
(delete-from-objects [objects]
|
||||
(if-let [target (get objects shape-id)]
|
||||
(let [parent-id (or (:parent-id target)
|
||||
(:frame-id target))
|
||||
children-ids (cph/get-children-ids objects shape-id)]
|
||||
(-> (reduce dissoc objects children-ids)
|
||||
(dissoc shape-id)
|
||||
(d/update-when parent-id delete-from-parent)))
|
||||
objects))]
|
||||
|
||||
(update container :objects delete-from-objects))))
|
||||
|
||||
(defn get-frames
|
||||
"Retrieves all frame objects as vector"
|
||||
[objects]
|
||||
|
@ -359,4 +394,3 @@
|
|||
(iterate next-pos
|
||||
(with-meta start-pos
|
||||
{:counter 0}))))
|
||||
|
||||
|
|
|
@ -65,3 +65,41 @@
|
|||
(t/is (= 0 (d/check-num [] 0)))
|
||||
(t/is (= 0 (d/check-num :foo 0)))
|
||||
(t/is (= 0 (d/check-num nil 0))))
|
||||
|
||||
(t/deftest insert-at-index
|
||||
;; insert different object
|
||||
(t/is (= (d/insert-at-index [:a :b] 1 [:c :d])
|
||||
[:a :c :d :b]))
|
||||
|
||||
;; insert on the start
|
||||
(t/is (= (d/insert-at-index [:a :b] 0 [:c])
|
||||
[:c :a :b]))
|
||||
|
||||
;; insert on the end 1
|
||||
(t/is (= (d/insert-at-index [:a :b] 2 [:c])
|
||||
[:a :b :c]))
|
||||
|
||||
;; insert on the end with not existing index
|
||||
(t/is (= (d/insert-at-index [:a :b] 10 [:c])
|
||||
[:a :b :c]))
|
||||
|
||||
;; insert existing in a contiguous index
|
||||
(t/is (= (d/insert-at-index [:a :b] 1 [:a])
|
||||
[:a :b]))
|
||||
|
||||
;; insert existing in the same index
|
||||
(t/is (= (d/insert-at-index [:a :b] 0 [:a])
|
||||
[:a :b]))
|
||||
|
||||
;; insert existing in other index case 1
|
||||
(t/is (= (d/insert-at-index [:a :b :c] 2 [:a])
|
||||
[:b :a :c]))
|
||||
|
||||
;; insert existing in other index case 2
|
||||
(t/is (= (d/insert-at-index [:a :b :c :d] 0 [:d])
|
||||
[:d :a :b :c]))
|
||||
|
||||
;; insert existing in other index case 3
|
||||
(t/is (= (d/insert-at-index [:a :b :c :d] 1 [:a])
|
||||
[:a :b :c :d])))
|
||||
|
||||
|
|
|
@ -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))]
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -10,50 +10,10 @@
|
|||
[clojure.pprint :refer [pprint]]
|
||||
[app.common.pages.helpers :as cph]))
|
||||
|
||||
(t/deftest insert-at-index
|
||||
;; insert different object
|
||||
(t/is (= (cph/insert-at-index [:a :b] 1 [:c :d])
|
||||
[:a :c :d :b]))
|
||||
|
||||
;; insert on the start
|
||||
(t/is (= (cph/insert-at-index [:a :b] 0 [:c])
|
||||
[:c :a :b]))
|
||||
|
||||
;; insert on the end 1
|
||||
(t/is (= (cph/insert-at-index [:a :b] 2 [:c])
|
||||
[:a :b :c]))
|
||||
|
||||
;; insert on the end with not existing index
|
||||
(t/is (= (cph/insert-at-index [:a :b] 10 [:c])
|
||||
[:a :b :c]))
|
||||
|
||||
;; insert existing in a contiguous index
|
||||
(t/is (= (cph/insert-at-index [:a :b] 1 [:a])
|
||||
[:a :b]))
|
||||
|
||||
;; insert existing in the same index
|
||||
(t/is (= (cph/insert-at-index [:a :b] 0 [:a])
|
||||
[:a :b]))
|
||||
|
||||
;; insert existing in other index case 1
|
||||
(t/is (= (cph/insert-at-index [:a :b :c] 2 [:a])
|
||||
[:b :a :c]))
|
||||
|
||||
;; insert existing in other index case 2
|
||||
(t/is (= (cph/insert-at-index [:a :b :c :d] 0 [:d])
|
||||
[:d :a :b :c]))
|
||||
|
||||
;; insert existing in other index case 3
|
||||
(t/is (= (cph/insert-at-index [:a :b :c :d] 1 [:a])
|
||||
[:a :b :c :d]))
|
||||
|
||||
)
|
||||
|
||||
|
||||
(t/deftest parse-path-name
|
||||
(t/is (= ["foo" "bar"] (cph/parse-path-name "foo/bar")))
|
||||
(t/is (= ["" "foo"] (cph/parse-path-name "foo")))
|
||||
(t/is (= ["" "foo"] (cph/parse-path-name "/foo")))
|
||||
(t/is (= ["" ""] (cph/parse-path-name "")))
|
||||
(t/is (= ["" ""] (cph/parse-path-name nil)))
|
||||
)
|
||||
(t/is (= ["" ""] (cph/parse-path-name nil))))
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)))
|
||||
|
||||
|
|
|
@ -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?)))))))))))
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)]
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
|
|
@ -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])))
|
||||
|
|
|
@ -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})
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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]}
|
||||
|
|
|
@ -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
|
||||
[:*
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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] []))]
|
||||
|
|
|
@ -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?
|
||||
|
|
|
@ -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]
|
||||
|
|
|
@ -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))]
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -7,15 +7,37 @@
|
|||
(ns frontend-tests.helpers.libraries
|
||||
(:require
|
||||
[app.common.pages.helpers :as cph]
|
||||
[app.common.types.component :as ctk]
|
||||
[app.common.types.container :as ctn]
|
||||
[app.common.types.file :as ctf]
|
||||
[app.main.data.workspace.state-helpers :as wsh]
|
||||
[cljs.pprint :refer [pprint]]
|
||||
[cljs.test :as t :include-macros true]
|
||||
[frontend-tests.helpers.pages :as thp]))
|
||||
|
||||
;; ---- Helpers to manage libraries and synchronization
|
||||
|
||||
(defn is-main-instance-root
|
||||
[shape]
|
||||
(t/is (nil? (:shape-ref shape)))
|
||||
(t/is (some? (:component-id shape)))
|
||||
(t/is (= (:component-root? shape) true)))
|
||||
|
||||
(defn is-main-instance-subroot
|
||||
[shape]
|
||||
(t/is (some? (:component-id shape))) ; shape-ref may or may be not nil
|
||||
(t/is (= (:component-root? shape) true)))
|
||||
|
||||
(defn is-main-instance-child
|
||||
[shape]
|
||||
(t/is (nil? (:component-id shape))) ; shape-ref may or may be not nil
|
||||
(t/is (nil? (:component-file shape)))
|
||||
(t/is (nil? (:component-root? shape))))
|
||||
|
||||
(defn is-main-instance-inner
|
||||
[shape]
|
||||
(if (some? (:component-id shape))
|
||||
(is-main-instance-subroot shape)
|
||||
(is-main-instance-child shape)))
|
||||
|
||||
(defn is-instance-root
|
||||
[shape]
|
||||
(t/is (some? (:shape-ref shape)))
|
||||
|
@ -58,27 +80,25 @@
|
|||
(defn resolve-instance
|
||||
"Get the shape with the given id and all its children, and
|
||||
verify that they are a well constructed instance tree."
|
||||
[state root-inst-id]
|
||||
(let [page (thp/current-page state)
|
||||
root-inst (ctn/get-shape page root-inst-id)
|
||||
shapes-inst (cph/get-children-with-self (:objects page)
|
||||
root-inst-id)]
|
||||
(is-instance-root (first shapes-inst))
|
||||
(run! is-instance-inner (rest shapes-inst))
|
||||
[state root-id]
|
||||
(let [page (thp/current-page state)
|
||||
shapes (cph/get-children-with-self (:objects page)
|
||||
root-id)]
|
||||
(is-instance-root (first shapes))
|
||||
(run! is-instance-inner (rest shapes))
|
||||
|
||||
shapes-inst))
|
||||
shapes))
|
||||
|
||||
(defn resolve-noninstance
|
||||
"Get the shape with the given id and all its children, and
|
||||
verify that they are not a component instance."
|
||||
[state root-inst-id]
|
||||
(let [page (thp/current-page state)
|
||||
root-inst (ctn/get-shape page root-inst-id)
|
||||
shapes-inst (cph/get-children-with-self (:objects page)
|
||||
root-inst-id)]
|
||||
(run! is-noninstance shapes-inst)
|
||||
[state root-id]
|
||||
(let [page (thp/current-page state)
|
||||
shapes (cph/get-children-with-self (:objects page)
|
||||
root-id)]
|
||||
(run! is-noninstance shapes)
|
||||
|
||||
shapes-inst))
|
||||
shapes))
|
||||
|
||||
(defn resolve-instance-and-main
|
||||
"Get the shape with the given id and all its children, and also
|
||||
|
@ -87,38 +107,39 @@
|
|||
(resolve-instance-and-main state root-inst-id false))
|
||||
|
||||
([state root-inst-id subinstance?]
|
||||
(let [page (thp/current-page state)
|
||||
root-inst (ctn/get-shape page root-inst-id)
|
||||
(let [page (thp/current-page state)
|
||||
root-inst (ctn/get-shape page root-inst-id)
|
||||
main-instance? (:main-instance? root-inst)
|
||||
|
||||
libs (wsh/get-libraries state)
|
||||
component (cph/get-component libs (:component-id root-inst))
|
||||
libs (wsh/get-libraries state)
|
||||
component (ctf/get-component libs (:component-id root-inst))
|
||||
library (ctf/get-component-library libs 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))
|
||||
|
||||
unique-refs (into #{} (map :shape-ref) shapes-inst)
|
||||
shapes-inst (cph/get-children-with-self (:objects page) root-inst-id)
|
||||
shapes-main (ctf/get-component-shapes (:data library) component)
|
||||
unique-refs (into #{} (map :shape-ref) shapes-inst)
|
||||
|
||||
main-exists? (fn [shape]
|
||||
(let [component-shape
|
||||
(cph/get-component-shape (:objects page) shape)
|
||||
|
||||
component
|
||||
(cph/get-component libs (:component-id component-shape))
|
||||
|
||||
main-shape
|
||||
(ctn/get-shape component (:shape-ref shape))]
|
||||
|
||||
(let [main-shape (ctf/get-ref-shape (:data library) component shape)]
|
||||
(t/is (some? main-shape))))]
|
||||
|
||||
;; Validate that the instance tree is well constructed
|
||||
(if subinstance?
|
||||
(is-instance-subroot (first shapes-inst))
|
||||
(is-instance-root (first shapes-inst)))
|
||||
(run! is-instance-inner (rest shapes-inst))
|
||||
(t/is (= (count shapes-inst)
|
||||
(count shapes-main)
|
||||
(count unique-refs)))
|
||||
(run! main-exists? shapes-inst)
|
||||
(if main-instance?
|
||||
(do
|
||||
(if subinstance?
|
||||
(is-main-instance-subroot (first shapes-inst))
|
||||
(is-main-instance-root (first shapes-inst)))
|
||||
(run! is-main-instance-inner (rest shapes-inst)))
|
||||
(do
|
||||
(if subinstance?
|
||||
(is-instance-subroot (first shapes-inst))
|
||||
(is-instance-root (first shapes-inst)))
|
||||
(run! is-instance-inner (rest shapes-inst))))
|
||||
|
||||
(t/is (= (count shapes-inst) (count shapes-main)))
|
||||
(when-not main-instance?
|
||||
(t/is (= (count shapes-inst) (count unique-refs)))
|
||||
(run! main-exists? shapes-inst))
|
||||
|
||||
[shapes-inst shapes-main component])))
|
||||
|
||||
|
@ -131,24 +152,11 @@
|
|||
root-inst (ctn/get-shape page root-inst-id)
|
||||
|
||||
libs (wsh/get-libraries state)
|
||||
component (cph/get-component libs (:component-id root-inst))
|
||||
component (ctf/get-component libs (:component-id root-inst))
|
||||
library (ctf/get-component-library libs 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))
|
||||
|
||||
unique-refs (into #{} (map :shape-ref) shapes-inst)
|
||||
|
||||
main-exists? (fn [shape]
|
||||
(let [component-shape
|
||||
(cph/get-component-shape (:objects page) shape)
|
||||
|
||||
component
|
||||
(cph/get-component libs (:component-id component-shape))
|
||||
|
||||
main-shape
|
||||
(ctn/get-shape component (:shape-ref shape))]
|
||||
|
||||
(t/is (some? main-shape))))]
|
||||
shapes-main (ctf/get-component-shapes (:data library) component)]
|
||||
|
||||
;; Validate that the instance tree is well constructed
|
||||
(is-instance-root (first shapes-inst))
|
||||
|
@ -158,14 +166,12 @@
|
|||
(defn resolve-component
|
||||
"Get the component with the given id and all its shapes."
|
||||
[state component-id]
|
||||
(let [page (thp/current-page state)
|
||||
libs (wsh/get-libraries state)
|
||||
component (cph/get-component libs component-id)
|
||||
root-main (ctk/get-component-root component)
|
||||
shapes-main (cph/get-children-with-self (:objects component) (:id root-main))]
|
||||
(let [libs (wsh/get-libraries state)
|
||||
component (ctf/get-component libs component-id)
|
||||
library (ctf/get-component-library libs component)
|
||||
shapes-main (ctf/get-component-shapes (:data library) component)]
|
||||
|
||||
;; Validate that the component tree is well constructed
|
||||
(run! is-noninstance shapes-main)
|
||||
|
||||
[shapes-main component]))
|
||||
|
||||
|
|
|
@ -7,20 +7,15 @@
|
|||
(ns frontend-tests.helpers.pages
|
||||
(:require
|
||||
[app.common.geom.point :as gpt]
|
||||
[app.common.geom.shapes :as gsh]
|
||||
[app.common.pages :as cp]
|
||||
[app.common.pages.changes-builder :as pcb]
|
||||
[app.common.pages.helpers :as cph]
|
||||
[app.common.types.shape :as cts]
|
||||
[app.common.uuid :as uuid]
|
||||
[app.main.data.workspace :as dw]
|
||||
[app.main.data.workspace.groups :as dwg]
|
||||
[app.main.data.workspace.layout :as layout]
|
||||
[app.main.data.workspace.libraries-helpers :as dwlh]
|
||||
[app.main.data.workspace.state-helpers :as wsh]
|
||||
[beicon.core :as rx]
|
||||
[cljs.pprint :refer [pprint]]
|
||||
[cljs.test :as t :include-macros true]
|
||||
[potok.core :as ptk]))
|
||||
[app.main.data.workspace.state-helpers :as wsh]))
|
||||
|
||||
;; ---- Helpers to manage pages and objects
|
||||
|
||||
|
@ -32,10 +27,12 @@
|
|||
:workspace-layout layout/default-layout
|
||||
:workspace-global layout/default-global
|
||||
:workspace-data {:id current-file-id
|
||||
:options {:components-v2 true}
|
||||
:components {}
|
||||
:pages []
|
||||
:pages-index {}}
|
||||
:workspace-libraries {}})
|
||||
:workspace-libraries {}
|
||||
:features {:components-v2 true}})
|
||||
|
||||
(def ^:private idmap (atom {}))
|
||||
|
||||
|
@ -56,6 +53,11 @@
|
|||
(let [page (current-page state)]
|
||||
(get-in page [:objects (id label)])))
|
||||
|
||||
(defn get-children
|
||||
[state label]
|
||||
(let [page (current-page state)]
|
||||
(cph/get-children (:objects page) (id label))))
|
||||
|
||||
(defn sample-page
|
||||
([state] (sample-page state {}))
|
||||
([state {:keys [id name] :as props
|
||||
|
@ -106,16 +108,17 @@
|
|||
objects (wsh/lookup-page-objects state (:id page))
|
||||
shapes (dwg/shapes-for-grouping objects shape-ids)
|
||||
|
||||
[group component-root changes]
|
||||
[group component-id changes]
|
||||
(dwlh/generate-add-component nil
|
||||
shapes
|
||||
(:objects page)
|
||||
(:id page)
|
||||
current-file-id
|
||||
true)]
|
||||
true
|
||||
dwg/prepare-create-group)]
|
||||
|
||||
(swap! idmap assoc instance-label (:id group)
|
||||
component-label (:id component-root))
|
||||
component-label component-id)
|
||||
(update state :workspace-data
|
||||
cp/process-changes (:redo-changes changes))))
|
||||
|
||||
|
@ -126,8 +129,10 @@
|
|||
(let [page (current-page state)
|
||||
libraries (wsh/get-libraries state)
|
||||
|
||||
changes (pcb/empty-changes nil (:id page))
|
||||
|
||||
[new-shape changes]
|
||||
(dwlh/generate-instantiate-component nil
|
||||
(dwlh/generate-instantiate-component changes
|
||||
file-id
|
||||
component-id
|
||||
(gpt/point 100 100)
|
||||
|
@ -148,7 +153,9 @@
|
|||
assoc library-id {:id library-id
|
||||
:name name
|
||||
:data {:id library-id
|
||||
:options (:options data)
|
||||
:pages (:pages data)
|
||||
:pages-index (:pages-index data)
|
||||
:components (:components data)}})
|
||||
(update :workspace-data
|
||||
assoc :components {} :pages [] :pages-index {}))))
|
||||
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -1,24 +1,19 @@
|
|||
(ns frontend-tests.state-components-test
|
||||
(:require
|
||||
[app.common.data :as d]
|
||||
[app.common.geom.point :as gpt]
|
||||
[app.common.pages.helpers :as cph]
|
||||
[app.common.types.container :as ctn]
|
||||
[app.common.types.file :as ctf]
|
||||
[app.main.data.workspace :as dw]
|
||||
[app.main.data.workspace.groups :as dwg]
|
||||
[app.main.data.workspace.libraries :as dwl]
|
||||
[app.main.data.workspace.libraries-helpers :as dwlh]
|
||||
[app.main.data.workspace.state-helpers :as wsh]
|
||||
[beicon.core :as rx]
|
||||
[cljs.pprint :refer [pprint]]
|
||||
[cljs.test :as t :include-macros true]
|
||||
[clojure.stacktrace :as stk]
|
||||
[frontend-tests.helpers.events :as the]
|
||||
[frontend-tests.helpers.libraries :as thl]
|
||||
[frontend-tests.helpers.pages :as thp]
|
||||
[linked.core :as lks]
|
||||
[potok.core :as ptk]))
|
||||
[app.common.geom.point :as gpt]
|
||||
[app.common.types.components-list :as ctkl]
|
||||
[app.common.types.container :as ctn]
|
||||
[app.common.types.file :as ctf]
|
||||
[app.main.data.workspace :as dw]
|
||||
[app.main.data.workspace.groups :as dwg]
|
||||
[app.main.data.workspace.libraries :as dwl]
|
||||
[app.main.data.workspace.state-helpers :as wsh]
|
||||
[cljs.test :as t :include-macros true]
|
||||
[frontend-tests.helpers.events :as the]
|
||||
[frontend-tests.helpers.libraries :as thl]
|
||||
[frontend-tests.helpers.pages :as thp]
|
||||
[linked.core :as lks]
|
||||
[potok.core :as ptk]))
|
||||
|
||||
(t/use-fixtures :each
|
||||
{:before thp/reset-idmap!})
|
||||
|
@ -37,7 +32,8 @@
|
|||
;; Uncomment to debug
|
||||
;; (ctf/dump-tree (get new-state :workspace-data)
|
||||
;; (get new-state :current-page-id)
|
||||
;; (get new-state :workspace-libraries))
|
||||
;; (get new-state :workspace-libraries)
|
||||
;; false true)
|
||||
|
||||
; Expected shape tree:
|
||||
;
|
||||
|
@ -54,8 +50,8 @@
|
|||
|
||||
[[group shape1] [c-group c-shape1] component]
|
||||
(thl/resolve-instance-and-main
|
||||
new-state
|
||||
(:parent-id shape1))
|
||||
new-state
|
||||
(:parent-id shape1))
|
||||
|
||||
file (wsh/get-local-file new-state)]
|
||||
|
||||
|
@ -73,40 +69,6 @@
|
|||
(dwl/add-component)
|
||||
:the/end)))))
|
||||
|
||||
;; Remove definitely when we ensure that the other method works
|
||||
;; well in more advanced tests.
|
||||
#_(t/deftest test-add-component-from-single-shape
|
||||
(t/async
|
||||
done
|
||||
(let [state (-> thp/initial-state
|
||||
(thp/sample-page)
|
||||
(thp/sample-shape :shape1 :rect
|
||||
{:name "Rect 1"}))]
|
||||
|
||||
(->> state
|
||||
(the/do-update (dw/select-shape (thp/id :shape1)))
|
||||
(the/do-watch-update dwl/add-component)
|
||||
(rx/do
|
||||
(fn [new-state]
|
||||
(let [shape1 (thp/get-shape new-state :shape1)
|
||||
|
||||
[[group shape1] [c-group c-shape1] component]
|
||||
(thl/resolve-instance-and-main
|
||||
new-state
|
||||
(:parent-id shape1))
|
||||
|
||||
file (wsh/get-local-file new-state)]
|
||||
|
||||
(t/is (= (:name shape1) "Rect 1"))
|
||||
(t/is (= (:name group) "Component 1"))
|
||||
(t/is (= (:name component) "Component 1"))
|
||||
(t/is (= (:name c-shape1) "Rect 1"))
|
||||
(t/is (= (:name c-group) "Component 1"))
|
||||
|
||||
(thl/is-from-file group file))))
|
||||
|
||||
(rx/subs done #(throw %))))))
|
||||
|
||||
(t/deftest test-add-component-from-several-shapes
|
||||
(t/async
|
||||
done
|
||||
|
@ -120,17 +82,14 @@
|
|||
store (the/prepare-store state done
|
||||
(fn [new-state]
|
||||
; Expected shape tree:
|
||||
;
|
||||
; [Page]
|
||||
; Root Frame
|
||||
; Component 1 #--> Component 1
|
||||
; Rect 1 ---> Rect 1
|
||||
; Rect-2 ---> Rect-2
|
||||
;
|
||||
; [Component 1]
|
||||
; Component 1
|
||||
; Rect 1
|
||||
; Rect-2
|
||||
; [Page]
|
||||
; Root Frame
|
||||
; Component 1
|
||||
; Rect 1
|
||||
; Rect-2
|
||||
;
|
||||
; [Component 1]
|
||||
; page1 / Component 1
|
||||
;
|
||||
(let [shape1 (thp/get-shape new-state :shape1)
|
||||
|
||||
|
@ -138,8 +97,8 @@
|
|||
[c-group c-shape1 c-shape2]
|
||||
component]
|
||||
(thl/resolve-instance-and-main
|
||||
new-state
|
||||
(:parent-id shape1))
|
||||
new-state
|
||||
(:parent-id shape1))
|
||||
|
||||
file (wsh/get-local-file new-state)]
|
||||
|
||||
|
@ -174,38 +133,36 @@
|
|||
(thp/id :shape2)]))
|
||||
|
||||
store (the/prepare-store state done
|
||||
(fn [new-state]
|
||||
(fn [new-state]
|
||||
; Expected shape tree:
|
||||
;
|
||||
; [Page]
|
||||
; Root Frame
|
||||
; Group #--> Group
|
||||
; Rect 1 ---> Rect 1
|
||||
; Rect-2 ---> Rect-2
|
||||
; [Page]
|
||||
; Root Frame
|
||||
; Group
|
||||
; Rect 1
|
||||
; Rect-2
|
||||
;
|
||||
; [Group]
|
||||
; page1 / Group
|
||||
;
|
||||
; [Group]
|
||||
; Group
|
||||
; Rect 1
|
||||
; Rect-2
|
||||
;
|
||||
(let [[[group shape1 shape2]
|
||||
[c-group c-shape1 c-shape2]
|
||||
component]
|
||||
(thl/resolve-instance-and-main
|
||||
new-state
|
||||
(thp/id :group1))
|
||||
(let [[[group shape1 shape2]
|
||||
[c-group c-shape1 c-shape2]
|
||||
component]
|
||||
(thl/resolve-instance-and-main
|
||||
new-state
|
||||
(thp/id :group1))
|
||||
|
||||
file (wsh/get-local-file new-state)]
|
||||
file (wsh/get-local-file new-state)]
|
||||
|
||||
(t/is (= (:name shape1) "Rect 1"))
|
||||
(t/is (= (:name shape2) "Rect-2"))
|
||||
(t/is (= (:name group) "Group"))
|
||||
(t/is (= (:name component) "Group"))
|
||||
(t/is (= (:name c-shape1) "Rect 1"))
|
||||
(t/is (= (:name c-shape2) "Rect-2"))
|
||||
(t/is (= (:name c-group) "Group"))
|
||||
(t/is (= (:name shape1) "Rect 1"))
|
||||
(t/is (= (:name shape2) "Rect-2"))
|
||||
(t/is (= (:name group) "Group"))
|
||||
(t/is (= (:name component) "Group"))
|
||||
(t/is (= (:name c-shape1) "Rect 1"))
|
||||
(t/is (= (:name c-shape2) "Rect-2"))
|
||||
(t/is (= (:name c-group) "Group"))
|
||||
|
||||
(thl/is-from-file group file))))]
|
||||
(thl/is-from-file group file))))]
|
||||
|
||||
(ptk/emit!
|
||||
store
|
||||
|
@ -220,42 +177,39 @@
|
|||
(thp/sample-page)
|
||||
(thp/sample-shape :shape1 :rect
|
||||
{:name "Rect 1"})
|
||||
(thp/make-component :instance1 :component1
|
||||
(thp/make-component :main1 :component1
|
||||
[(thp/id :shape1)]))
|
||||
|
||||
store (the/prepare-store state done
|
||||
(fn [new-state]
|
||||
; Expected shape tree:
|
||||
;
|
||||
; [Page]
|
||||
; Root Frame
|
||||
; Rect 1 #--> Rect 1
|
||||
; Rect 1 @--> Rect 1
|
||||
; Rect 1 ---> Rect 1
|
||||
;
|
||||
; [Rect 1]
|
||||
; Rect 1
|
||||
; Rect 1
|
||||
;
|
||||
; [Rect 1]
|
||||
; Rect 1
|
||||
; Rect 1 @--> Rect 1
|
||||
; Rect 1 ---> Rect 1
|
||||
; [Page]
|
||||
; Root Frame
|
||||
; Rect 1
|
||||
; Rect 1
|
||||
; Rect 1
|
||||
;
|
||||
; [Rect 1]
|
||||
; page1 / Rect 1
|
||||
;
|
||||
; [Rect 1]
|
||||
; page1 / Rect 1
|
||||
;
|
||||
(let [[[instance1 shape1]
|
||||
[c-instance1 c-shape1]
|
||||
component1]
|
||||
(thl/resolve-instance-and-main
|
||||
new-state
|
||||
(thp/id :instance1)
|
||||
true)
|
||||
new-state
|
||||
(thp/id :main1)
|
||||
true)
|
||||
|
||||
[[instance2 instance1' shape1']
|
||||
[c-instance2 c-instance1' c-shape1']
|
||||
component2]
|
||||
(thl/resolve-instance-and-main
|
||||
new-state
|
||||
(:parent-id instance1))]
|
||||
new-state
|
||||
(:parent-id instance1))]
|
||||
|
||||
(t/is (= (:name shape1) "Rect 1"))
|
||||
(t/is (= (:name instance1) "Rect 1"))
|
||||
|
@ -273,7 +227,7 @@
|
|||
|
||||
(ptk/emit!
|
||||
store
|
||||
(dw/select-shape (thp/id :instance1))
|
||||
(dw/select-shape (thp/id :main1))
|
||||
(dwl/add-component)
|
||||
:the/end))))
|
||||
|
||||
|
@ -284,34 +238,34 @@
|
|||
(thp/sample-page)
|
||||
(thp/sample-shape :shape1 :rect
|
||||
{:name "Rect 1"})
|
||||
(thp/make-component :instance1 :component-1
|
||||
(thp/make-component :main1 :component1
|
||||
[(thp/id :shape1)]))
|
||||
|
||||
instance1 (thp/get-shape state :instance1)
|
||||
main1 (thp/get-shape state :main1)
|
||||
|
||||
store (the/prepare-store state done
|
||||
(fn [new-state]
|
||||
; Expected shape tree:
|
||||
;
|
||||
; [Page]
|
||||
; Root Frame
|
||||
; Rect-2 #--> Renamed component
|
||||
; Rect 1 ---> Rect 1
|
||||
;
|
||||
; [Renamed]
|
||||
; Renamed component
|
||||
; Rect 1
|
||||
(let [libs (wsh/get-libraries new-state)
|
||||
component (cph/get-component libs
|
||||
(:component-file instance1)
|
||||
(:component-id instance1))]
|
||||
(t/is (= (:name component)
|
||||
"Renamed component")))))]
|
||||
(fn [new-state]
|
||||
; Expected shape tree:
|
||||
;
|
||||
; [Page]
|
||||
; Root Frame
|
||||
; Rect 1
|
||||
; Rect 1
|
||||
;
|
||||
; [Renamed component]
|
||||
; page1 / Rect 1
|
||||
;
|
||||
(let [libs (wsh/get-libraries new-state)
|
||||
component (ctf/get-component libs
|
||||
(:component-file main1)
|
||||
(:component-id main1))]
|
||||
(t/is (= (:name component)
|
||||
"Renamed component")))))]
|
||||
|
||||
(ptk/emit!
|
||||
store
|
||||
(dwl/rename-component (:component-id instance1) "Renamed component")
|
||||
:the/end))))
|
||||
store
|
||||
(dwl/rename-component (:component-id main1) "Renamed component")
|
||||
:the/end))))
|
||||
|
||||
(t/deftest test-duplicate-component
|
||||
(t/async
|
||||
|
@ -320,28 +274,28 @@
|
|||
(thp/sample-page)
|
||||
(thp/sample-shape :shape1 :rect
|
||||
{:name "Rect 1"})
|
||||
(thp/make-component :instance1 :component-1
|
||||
(thp/make-component :main1 :component1
|
||||
[(thp/id :shape1)]))
|
||||
|
||||
instance1 (thp/get-shape state :instance1)
|
||||
component-id (:component-id instance1)
|
||||
main1 (thp/get-shape state :main1)
|
||||
component-id (:component-id main1)
|
||||
|
||||
store (the/prepare-store state done
|
||||
(fn [new-state]
|
||||
; Expected shape tree:
|
||||
;
|
||||
; [Page]
|
||||
; Root Frame
|
||||
; Rect 1 #--> Rect 1
|
||||
; Rect 1 ---> Rect 1
|
||||
;
|
||||
; [Page]
|
||||
; Root Frame
|
||||
; Rect 1
|
||||
; Rect 1
|
||||
; Rect 1 #--> Rect 1
|
||||
; Rect 1 ---> Rect 1
|
||||
;
|
||||
; [Rect 1]
|
||||
; Rect 1
|
||||
; Rect 1
|
||||
;
|
||||
; [Rect 1]
|
||||
; Rect 1
|
||||
; Rect 1
|
||||
; page1 / Rect 1
|
||||
;
|
||||
; [Rect 1]
|
||||
; page1 / Rect 1
|
||||
;
|
||||
(let [new-component-id (->> (get-in new-state
|
||||
[:workspace-data
|
||||
|
@ -350,24 +304,24 @@
|
|||
(filter #(not= % component-id))
|
||||
(first))
|
||||
|
||||
[[instance1 shape1]
|
||||
[c-instance1 c-shape1]
|
||||
component1]
|
||||
[[_instance1 _shape1]
|
||||
[_c-instance1 _c-shape1]
|
||||
_component1]
|
||||
(thl/resolve-instance-and-main
|
||||
new-state
|
||||
(:id instance1))
|
||||
new-state
|
||||
(:id main1))
|
||||
|
||||
[[c-component2 c-shape2]
|
||||
[[_c-component2 _c-shape2]
|
||||
component2]
|
||||
(thl/resolve-component
|
||||
new-state
|
||||
new-component-id)]
|
||||
new-state
|
||||
new-component-id)]
|
||||
|
||||
(t/is (= (:name component2) "Rect 1")))))]
|
||||
|
||||
(ptk/emit!
|
||||
store
|
||||
(dwl/duplicate-component {:id component-id})
|
||||
(dwl/duplicate-component thp/current-file-id component-id)
|
||||
:the/end))))
|
||||
|
||||
(t/deftest test-delete-component
|
||||
|
@ -377,42 +331,115 @@
|
|||
(thp/sample-page)
|
||||
(thp/sample-shape :shape1 :rect
|
||||
{:name "Rect 1"})
|
||||
(thp/make-component :instance1 :component-1
|
||||
[(thp/id :shape1)]))
|
||||
|
||||
file (wsh/get-local-file state)
|
||||
|
||||
instance1 (thp/get-shape state :instance1)
|
||||
component-id (:component-id instance1)
|
||||
(thp/make-component :main1 :component1
|
||||
[(thp/id :shape1)])
|
||||
(thp/instantiate-component :instance1
|
||||
(thp/id :component1)))
|
||||
|
||||
store (the/prepare-store state done
|
||||
(fn [new-state]
|
||||
; Expected shape tree:
|
||||
;
|
||||
; [Page]
|
||||
; Root Frame
|
||||
; Rect-2
|
||||
; Rect 1
|
||||
;
|
||||
(let [[instance1 shape1]
|
||||
(thl/resolve-noninstance
|
||||
new-state
|
||||
(:id instance1))
|
||||
(fn [new-state]
|
||||
; Expected shape tree:
|
||||
;
|
||||
; [Page]
|
||||
; Root Frame
|
||||
; Rect 1 #--> ?
|
||||
; Rect 1 ---> ?
|
||||
;
|
||||
(let [[main1 shape1]
|
||||
(thl/resolve-noninstance
|
||||
new-state
|
||||
(thp/id :main1))
|
||||
|
||||
libs (wsh/get-libraries new-state)
|
||||
component (cph/get-component libs
|
||||
(:component-file instance1)
|
||||
(:component-id instance1))]
|
||||
[[instance1 shape2] [c-instance1 c-shape2] component1]
|
||||
(thl/resolve-instance-and-main-allow-dangling
|
||||
new-state
|
||||
(thp/id :instance1))
|
||||
|
||||
(t/is (some? instance1))
|
||||
(t/is (some? shape1))
|
||||
(t/is (nil? component)))))]
|
||||
file (wsh/get-local-file new-state)
|
||||
component2 (ctkl/get-component file (thp/id :component1))
|
||||
component3 (ctkl/get-deleted-component file (thp/id :component1))
|
||||
|
||||
saved-objects (:objects component3)
|
||||
saved-main1 (get saved-objects (:shape-ref instance1))
|
||||
saved-shape2 (get saved-objects (:shape-ref shape2))]
|
||||
|
||||
(t/is (nil? main1))
|
||||
(t/is (nil? shape1))
|
||||
|
||||
(t/is (= (:name instance1) "Rect 1"))
|
||||
(t/is (= (:touched instance1) nil))
|
||||
(t/is (not= (:shape-ref instance1) nil))
|
||||
(t/is (= (:name shape2) "Rect 1"))
|
||||
(t/is (= (:touched shape2) nil))
|
||||
(t/is (not= (:shape-ref shape2) nil))
|
||||
(t/is (nil? c-instance1))
|
||||
(t/is (nil? c-shape2))
|
||||
(t/is (nil? component1))
|
||||
|
||||
(t/is (nil? component2))
|
||||
|
||||
(t/is (= (:name component3) "Rect 1"))
|
||||
(t/is (= (:deleted component3) true))
|
||||
(t/is (some? (:objects component3)))
|
||||
|
||||
(t/is (= (:name saved-main1) "Rect 1"))
|
||||
(t/is (= (:name saved-shape2) "Rect 1")))))]
|
||||
|
||||
(ptk/emit!
|
||||
store
|
||||
(dwl/delete-component {:id component-id})
|
||||
(dwl/sync-file (:id file) (:id file) :components component-id)
|
||||
:the/end))))
|
||||
store
|
||||
(dwl/delete-component {:id (thp/id :component1)})
|
||||
:the/end))))
|
||||
|
||||
(t/deftest test-restore-component
|
||||
(t/async
|
||||
done
|
||||
(let [state (-> thp/initial-state
|
||||
(thp/sample-page)
|
||||
(thp/sample-shape :shape1 :rect
|
||||
{:name "Rect 1"})
|
||||
(thp/make-component :main1 :component1
|
||||
[(thp/id :shape1)])
|
||||
(thp/instantiate-component :instance1
|
||||
(thp/id :component1)))
|
||||
|
||||
store (the/prepare-store state done
|
||||
(fn [new-state]
|
||||
; Expected shape tree:
|
||||
;
|
||||
; [Page]
|
||||
; Root Frame
|
||||
; Rect 1 #--> Rect 1
|
||||
; Rect 1 ---> Rect 1
|
||||
; Rect 1
|
||||
; Rect 1
|
||||
;
|
||||
; [Rect 1]
|
||||
; page1 / Rect 1
|
||||
;
|
||||
(let [[[instance1 shape2] [c-instance1 c-shape2] component1]
|
||||
(thl/resolve-instance-and-main
|
||||
new-state
|
||||
(thp/id :instance1))
|
||||
|
||||
file (wsh/get-local-file new-state)
|
||||
component2 (ctkl/get-component file (thp/id :component1))
|
||||
|
||||
saved-objects (:objects component2)]
|
||||
|
||||
(t/is (= (:name instance1) "Rect 1"))
|
||||
(t/is (= (:name shape2) "Rect 1"))
|
||||
(t/is (= (:name c-instance1) "Rect 1"))
|
||||
(t/is (= (:name c-shape2) "Rect 1"))
|
||||
|
||||
(t/is (some? component1))
|
||||
(t/is (some? component2))
|
||||
(t/is (nil? saved-objects)))))]
|
||||
|
||||
(ptk/emit!
|
||||
store
|
||||
(dwl/delete-component {:id (thp/id :component1)})
|
||||
(dwl/restore-component thp/current-file-id (thp/id :component1))
|
||||
:the/end))))
|
||||
|
||||
(t/deftest test-instantiate-component
|
||||
(t/async
|
||||
|
@ -421,46 +448,45 @@
|
|||
(thp/sample-page)
|
||||
(thp/sample-shape :shape1 :rect
|
||||
{:name "Rect 1"})
|
||||
(thp/make-component :instance1 :component-1
|
||||
(thp/make-component :main1 :component1
|
||||
[(thp/id :shape1)]))
|
||||
|
||||
file (wsh/get-local-file state)
|
||||
component-id (thp/id :component-1)
|
||||
instance1 (thp/get-shape state :instance1)
|
||||
component-id (thp/id :component1)
|
||||
main1 (thp/get-shape state :main1)
|
||||
|
||||
store (the/prepare-store state done
|
||||
(fn [new-state]
|
||||
; Expected shape tree:
|
||||
;
|
||||
; [Page]
|
||||
; Root Frame
|
||||
; [Page]
|
||||
; Root Frame
|
||||
; Rect 1
|
||||
; Rect 1
|
||||
; Rect 1 #--> Rect 1
|
||||
; Rect 1 ---> Rect 1
|
||||
; Rect 1 #--> Rect 1
|
||||
; Rect 1 ---> Rect 1
|
||||
;
|
||||
; [Rect 1]
|
||||
; Rect 1
|
||||
; Rect 1
|
||||
; Rect 1 ---> Rect 1
|
||||
;
|
||||
; [Rect 1]
|
||||
; page1 / Rect 1
|
||||
;
|
||||
(let [new-instance-id (-> new-state
|
||||
wsh/lookup-selected
|
||||
first)
|
||||
|
||||
[[instance2 shape2]
|
||||
[c-instance2 c-shape2]
|
||||
[[instance1 shape2]
|
||||
[c-instance1 c-shape2]
|
||||
component]
|
||||
(thl/resolve-instance-and-main
|
||||
new-state
|
||||
new-instance-id)]
|
||||
new-state
|
||||
new-instance-id)]
|
||||
|
||||
(t/is (not= (:id instance1) (:id instance2)))
|
||||
(t/is (not= (:id main1) (:id instance1)))
|
||||
(t/is (= (:id component) component-id))
|
||||
(t/is (= (:name instance2) "Rect 1"))
|
||||
(t/is (= (:name instance1) "Rect 1"))
|
||||
(t/is (= (:name shape2) "Rect 1"))
|
||||
(t/is (= (:name c-instance2) "Rect 1"))
|
||||
(t/is (= (:name c-instance1) "Rect 1"))
|
||||
(t/is (= (:name c-shape2) "Rect 1"))
|
||||
(t/is (= (:component-file instance2)
|
||||
(t/is (= (:component-file instance1)
|
||||
thp/current-file-id)))))]
|
||||
|
||||
(ptk/emit!
|
||||
|
@ -477,13 +503,13 @@
|
|||
(thp/sample-page)
|
||||
(thp/sample-shape :shape1 :rect
|
||||
{:name "Rect 1"})
|
||||
(thp/make-component :instance1 :component-1
|
||||
(thp/make-component :main1 :component1
|
||||
[(thp/id :shape1)])
|
||||
(thp/move-to-library :lib1 "Library 1")
|
||||
(thp/sample-page))
|
||||
|
||||
library-id (thp/id :lib1)
|
||||
component-id (thp/id :component-1)
|
||||
component-id (thp/id :component1)
|
||||
|
||||
store (the/prepare-store state done
|
||||
(fn [new-state]
|
||||
|
@ -498,19 +524,19 @@
|
|||
wsh/lookup-selected
|
||||
first)
|
||||
|
||||
[[instance2 shape2]
|
||||
[c-instance2 c-shape2]
|
||||
[[instance1 shape2]
|
||||
[c-instance1 c-shape2]
|
||||
component]
|
||||
(thl/resolve-instance-and-main
|
||||
new-state
|
||||
new-instance-id)]
|
||||
|
||||
(t/is (= (:id component) component-id))
|
||||
(t/is (= (:name instance2) "Rect 1"))
|
||||
(t/is (= (:name instance1) "Rect 1"))
|
||||
(t/is (= (:name shape2) "Rect 1"))
|
||||
(t/is (= (:name c-instance2) "Rect 1"))
|
||||
(t/is (= (:name c-instance1) "Rect 1"))
|
||||
(t/is (= (:name c-shape2) "Rect 1"))
|
||||
(t/is (= (:component-file instance2) library-id)))))]
|
||||
(t/is (= (:component-file instance1) library-id)))))]
|
||||
|
||||
(ptk/emit!
|
||||
store
|
||||
|
@ -526,32 +552,34 @@
|
|||
(thp/sample-page)
|
||||
(thp/sample-shape :shape1 :rect
|
||||
{:name "Rect 1"})
|
||||
(thp/make-component :instance1 :component-1
|
||||
[(thp/id :shape1)]))
|
||||
(thp/make-component :main1 :component1
|
||||
[(thp/id :shape1)])
|
||||
(thp/instantiate-component :instance1
|
||||
(thp/id :component1)))
|
||||
|
||||
instance1 (thp/get-shape state :instance1)
|
||||
component-id (:component-id instance1)
|
||||
instance1 (thp/get-shape state :instance1)
|
||||
|
||||
store (the/prepare-store state done
|
||||
(fn [new-state]
|
||||
; Expected shape tree:
|
||||
;
|
||||
; [Page]
|
||||
; Root Frame
|
||||
; Rect-2
|
||||
; Rect 1
|
||||
;
|
||||
; [Rect-2]
|
||||
; Rect-2
|
||||
; Rect 1
|
||||
;
|
||||
(let [[instance1 shape1]
|
||||
(thl/resolve-noninstance
|
||||
new-state
|
||||
(:id instance1))]
|
||||
(fn [new-state]
|
||||
; Expected shape tree:
|
||||
;
|
||||
; [Page]
|
||||
; Root Frame
|
||||
; Rect 1
|
||||
; Rect 1
|
||||
; Rect 1
|
||||
; Rect 1
|
||||
;
|
||||
; [Rect 1]
|
||||
; page1 / Rect 1
|
||||
;
|
||||
(let [[instance2 shape1]
|
||||
(thl/resolve-noninstance
|
||||
new-state
|
||||
(:id instance1))]
|
||||
|
||||
(t/is (some? instance1))
|
||||
(t/is (some? shape1)))))]
|
||||
(t/is (some? instance2))
|
||||
(t/is (some? shape1)))))]
|
||||
|
||||
(ptk/emit!
|
||||
store
|
||||
|
@ -566,28 +594,21 @@
|
|||
(thp/sample-shape :shape1 :rect
|
||||
{:name "Rect 1"}))
|
||||
|
||||
file (wsh/get-local-file state)
|
||||
instance1 (thp/get-shape state :instance1)
|
||||
component-id (:component-id instance1)
|
||||
|
||||
store (the/prepare-store state done
|
||||
(fn [new-state]
|
||||
; Expected shape tree:
|
||||
;
|
||||
; [Page]
|
||||
; Root Frame
|
||||
; Group #--> Group
|
||||
; Rect 1 @--> Rect 1
|
||||
; Rect 1 ---> Rect 1
|
||||
;
|
||||
; [Page]
|
||||
; Root Frame
|
||||
; Group
|
||||
; Rect 1
|
||||
; Rect 1
|
||||
;
|
||||
; [Rect 1]
|
||||
; Rect 1
|
||||
; Rect 1
|
||||
;
|
||||
; [Group]
|
||||
; Group
|
||||
; Rect 1 @--> Rect 1
|
||||
; Rect 1 ---> Rect 1
|
||||
; page1 / Rect 1
|
||||
;
|
||||
; [Group]
|
||||
; page1 / Group
|
||||
;
|
||||
(let [page (thp/current-page new-state)
|
||||
shape1 (thp/get-shape new-state :shape1)
|
||||
|
@ -623,46 +644,41 @@
|
|||
(thp/sample-page)
|
||||
(thp/sample-shape :shape1 :rect
|
||||
{:name "Rect 1"})
|
||||
(thp/make-component :instance1 :component-1
|
||||
(thp/make-component :main1 :component1
|
||||
[(thp/id :shape1)])
|
||||
(thp/group-shapes :group1
|
||||
[(thp/id :instance1)])
|
||||
(thp/make-component :instance2 :component-2
|
||||
[(thp/id :group1)]))
|
||||
(thp/make-component :main2 :component-2
|
||||
[(thp/id :main1)]))
|
||||
|
||||
file (wsh/get-local-file state)
|
||||
instance1 (thp/get-shape state :instance1)
|
||||
instance2 (thp/get-shape state :instance2)
|
||||
component-id (:component-id instance2)
|
||||
main1 (thp/get-shape state :main1)
|
||||
main2 (thp/get-shape state :main2)
|
||||
component-id (:component-id main2)
|
||||
|
||||
store (the/prepare-store state done
|
||||
(fn [new-state]
|
||||
; Expected shape tree:
|
||||
;
|
||||
; [Page]
|
||||
; Root Frame
|
||||
; Rect 1 #--> Rect 1
|
||||
; Rect 1 @--> Rect 1
|
||||
; Rect 1 ---> Rect 1
|
||||
; Rect 1 #--> Rect 1
|
||||
; Rect 1 @--> Rect 1
|
||||
; Rect 1 ---> Rect 1
|
||||
;
|
||||
; [Page]
|
||||
; Root Frame
|
||||
; Rect 1
|
||||
; Rect 1
|
||||
; Rect 1
|
||||
; Rect 1 #--> Rect 1
|
||||
; Rect 1 @--> Rect 1
|
||||
; Rect 1 ---> Rect 1
|
||||
;
|
||||
; [Rect 1]
|
||||
; Rect 1
|
||||
; Rect 1
|
||||
;
|
||||
; [Rect 1]
|
||||
; Rect 1
|
||||
; Rect 1 @--> Rect 1
|
||||
; Rect 1 ---> Rect 1
|
||||
; page1 / Rect 1
|
||||
;
|
||||
; [Rect 1]
|
||||
; page1 / Rect 1
|
||||
;
|
||||
(let [new-instance-id (-> new-state
|
||||
wsh/lookup-selected
|
||||
first)
|
||||
|
||||
[[instance3 shape3 shape4]
|
||||
[c-instance3 c-shape3 c-shape4]
|
||||
[[instance1 shape1 shape2]
|
||||
[c-instance1 c-shape1 c-shape2]
|
||||
component]
|
||||
(thl/resolve-instance-and-main
|
||||
new-state
|
||||
|
@ -670,19 +686,19 @@
|
|||
|
||||
; TODO: get and check the instance inside component [Rect-2]
|
||||
|
||||
(t/is (not= (:id instance1) (:id instance3)))
|
||||
(t/is (not= (:id main1) (:id instance1)))
|
||||
(t/is (= (:id component) component-id))
|
||||
(t/is (= (:name instance3) "Rect 1"))
|
||||
(t/is (= (:name shape3) "Rect 1"))
|
||||
(t/is (= (:name shape4) "Rect 1"))
|
||||
(t/is (= (:name c-instance3) "Rect 1"))
|
||||
(t/is (= (:name c-shape3) "Rect 1"))
|
||||
(t/is (= (:name c-shape4) "Rect 1")))))]
|
||||
(t/is (= (:name instance1) "Rect 1"))
|
||||
(t/is (= (:name shape1) "Rect 1"))
|
||||
(t/is (= (:name shape2) "Rect 1"))
|
||||
(t/is (= (:name c-instance1) "Rect 1"))
|
||||
(t/is (= (:name c-shape1) "Rect 1"))
|
||||
(t/is (= (:name c-shape2) "Rect 1")))))]
|
||||
|
||||
(ptk/emit!
|
||||
store
|
||||
(dwl/instantiate-component (:id file)
|
||||
(:component-id instance2)
|
||||
(:component-id main2)
|
||||
(gpt/point 100 100))
|
||||
:the/end))))
|
||||
|
||||
|
@ -693,38 +709,36 @@
|
|||
(thp/sample-page)
|
||||
(thp/sample-shape :shape1 :rect
|
||||
{:name "Rect 1"})
|
||||
(thp/make-component :instance1 :component-1
|
||||
(thp/make-component :main1 :component1
|
||||
[(thp/id :shape1)])
|
||||
(thp/move-to-library :lib1 "Library 1")
|
||||
(thp/sample-page)
|
||||
(thp/instantiate-component :instance2
|
||||
(thp/id :component-1)
|
||||
(thp/instantiate-component :instance1
|
||||
(thp/id :component1)
|
||||
(thp/id :lib1)))
|
||||
|
||||
library-id (thp/id :lib1)
|
||||
component-id (thp/id :component-1)
|
||||
file (wsh/get-local-file state)
|
||||
library-id (thp/id :lib1)
|
||||
|
||||
store (the/prepare-store state done
|
||||
(fn [new-state]
|
||||
; Expected shape tree:
|
||||
;
|
||||
; [Page]
|
||||
; Root Frame
|
||||
; Group #--> Group
|
||||
; Rect 1 @--> <Library 1> Rect 1
|
||||
; Rect 1 ---> <Library 1> Rect 1
|
||||
; [Page]
|
||||
; Root Frame
|
||||
; Group
|
||||
; Rect 1 #--> <Library 1> Rect 1
|
||||
; Rect 1 ---> <Library 1> Rect 1
|
||||
;
|
||||
; [Group]
|
||||
; page1 / Group
|
||||
;
|
||||
; [Group]
|
||||
; Group
|
||||
; Rect 1 @--> <Library 1> Rect 1
|
||||
; Rect 1 ---> <Library 1> Rect 1
|
||||
;
|
||||
(let [instance2 (thp/get-shape new-state :instance2)
|
||||
(let [instance1 (thp/get-shape new-state :instance1)
|
||||
|
||||
[[group1 shape1 shape2] [c-group1 c-shape1 c-shape2] component]
|
||||
[[group1 shape1 shape2] [c-group1 c-shape1 c-shape2] _component]
|
||||
(thl/resolve-instance-and-main
|
||||
new-state
|
||||
(:parent-id instance2))]
|
||||
(:parent-id instance1))]
|
||||
|
||||
(t/is (= (:name group1) "Group"))
|
||||
(t/is (= (:name shape1) "Rect 1"))
|
||||
|
@ -735,14 +749,13 @@
|
|||
(t/is (= (:component-file group1) thp/current-file-id))
|
||||
(t/is (= (:component-file shape1) library-id))
|
||||
(t/is (= (:component-file shape2) nil))
|
||||
(t/is (= (:component-file c-group1) nil))
|
||||
(t/is (= (:component-file c-group1) (:id file)))
|
||||
(t/is (= (:component-file c-shape1) library-id))
|
||||
(t/is (= (:component-file c-shape2) nil)))))]
|
||||
|
||||
(ptk/emit!
|
||||
store
|
||||
(dw/select-shape (thp/id :instance2))
|
||||
(dw/select-shape (thp/id :instance1))
|
||||
dwg/group-selected
|
||||
(dwl/add-component)
|
||||
:the/end))))
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue