mirror of
https://github.com/penpot/penpot.git
synced 2025-03-18 10:41:29 -05:00
🎉 Import & export new components
This commit is contained in:
parent
251e7eada2
commit
46053b6bbf
15 changed files with 283 additions and 122 deletions
|
@ -750,6 +750,10 @@
|
|||
(uuid? (:typography-ref-file form))
|
||||
(update :typography-ref-file lookup-index)
|
||||
|
||||
;; This covers the component instance links
|
||||
(uuid? (:component-file form))
|
||||
(update :component-file lookup-index)
|
||||
|
||||
;; This covers the shadows and grids (they have directly
|
||||
;; the :file-id prop)
|
||||
(uuid? (:file-id form))
|
||||
|
|
|
@ -9,12 +9,16 @@
|
|||
(:require
|
||||
[app.common.data :as d]
|
||||
[app.common.geom.matrix :as gmt]
|
||||
[app.common.geom.point :as gpt]
|
||||
[app.common.geom.shapes :as gsh]
|
||||
[app.common.pages.changes :as ch]
|
||||
[app.common.pages.changes-spec :as pcs]
|
||||
[app.common.spec :as us]
|
||||
[app.common.types.components-list :as ctkl]
|
||||
[app.common.types.container :as ctn]
|
||||
[app.common.types.file :as ctf]
|
||||
[app.common.types.page :as ctp]
|
||||
[app.common.types.pages-list :as ctpl]
|
||||
[app.common.types.shape :as cts]
|
||||
[app.common.uuid :as uuid]
|
||||
[cuerdas.core :as str]))
|
||||
|
@ -516,10 +520,17 @@
|
|||
[file data]
|
||||
|
||||
(let [selrect cts/empty-selrect
|
||||
name (:name data)
|
||||
path (:path data)
|
||||
name (:name data)
|
||||
path (:path data)
|
||||
main-instance-id (:main-instance-id data)
|
||||
main-instance-page (:main-instance-page data)
|
||||
obj (-> (cts/make-minimal-group nil selrect name)
|
||||
(merge data)
|
||||
(dissoc :path
|
||||
:main-instance-id
|
||||
:main-instance-page
|
||||
:main-instance-x
|
||||
:main-instance-y)
|
||||
(check-name file :group)
|
||||
(d/without-nils))]
|
||||
(-> file
|
||||
|
@ -528,6 +539,8 @@
|
|||
:id (:id obj)
|
||||
:name name
|
||||
:path path
|
||||
:main-instance-id main-instance-id
|
||||
:main-instance-page main-instance-page
|
||||
:shapes [obj]})
|
||||
|
||||
(assoc :last-id (:id obj))
|
||||
|
@ -546,7 +559,8 @@
|
|||
(commit-change
|
||||
file
|
||||
{:type :del-component
|
||||
:id component-id})
|
||||
:id component-id
|
||||
:skip-undelete? true})
|
||||
|
||||
(:masked-group? component)
|
||||
(let [mask (first children)]
|
||||
|
@ -586,6 +600,42 @@
|
|||
(dissoc :current-component-id)
|
||||
(update :parent-stack pop))))
|
||||
|
||||
(defn finish-deleted-component
|
||||
[component-id page-id main-instance-x main-instance-y file]
|
||||
(let [file (assoc file :current-component-id component-id)
|
||||
page (ctpl/get-page (:data file) page-id)
|
||||
component (ctkl/get-component (:data file) component-id)
|
||||
main-instance-id (:main-instance-id component)
|
||||
|
||||
; To obtain a deleted component, we first create the component
|
||||
; and the main instance in the workspace, and then delete them.
|
||||
[_ shapes]
|
||||
(ctn/make-component-instance page
|
||||
component
|
||||
(:id file)
|
||||
(gpt/point main-instance-x
|
||||
main-instance-y)
|
||||
{:main-instance? true
|
||||
:force-id main-instance-id})]
|
||||
(as-> file $
|
||||
(reduce #(commit-change %1
|
||||
{:type :add-obj
|
||||
:id (:id %2)
|
||||
:page-id (:id page)
|
||||
:parent-id (:parent-id %2)
|
||||
:frame-id (:frame-id %2)
|
||||
:obj %2})
|
||||
$
|
||||
shapes)
|
||||
(commit-change $ {:type :del-component
|
||||
:id component-id})
|
||||
(reduce #(commit-change %1 {:type :del-obj
|
||||
:page-id page-id
|
||||
:id (:id %2)})
|
||||
$
|
||||
shapes)
|
||||
(dissoc $ :current-component-id))))
|
||||
|
||||
(defn delete-object
|
||||
[file id]
|
||||
(let [page-id (:current-page-id file)]
|
||||
|
|
|
@ -370,8 +370,8 @@
|
|||
(assoc :objects objects))))
|
||||
|
||||
(defmethod process-change :del-component
|
||||
[data {:keys [id]}]
|
||||
(ctf/delete-component data id))
|
||||
[data {:keys [id skip-undelete?]}]
|
||||
(ctf/delete-component data id skip-undelete?))
|
||||
|
||||
(defmethod process-change :restore-component
|
||||
[data {:keys [id]}]
|
||||
|
|
|
@ -159,8 +159,11 @@
|
|||
(s/keys :req-un [::id]
|
||||
:opt-un [::name :internal.changes.add-component/shapes]))
|
||||
|
||||
(s/def :internal.changes.del-component/skip-undelete? boolean?)
|
||||
|
||||
(defmethod change-spec :del-component [_]
|
||||
(s/keys :req-un [::id]))
|
||||
(s/keys :req-un [::id]
|
||||
:opt-un [:internal.changes.del-component/skip-undelete?]))
|
||||
|
||||
(defmethod change-spec :restore-component [_]
|
||||
(s/keys :req-un [::id]))
|
||||
|
|
|
@ -108,53 +108,59 @@
|
|||
"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 main-instance?]
|
||||
(let [component-shape (get-shape component (:id component))
|
||||
([container component component-file-id position]
|
||||
(make-component-instance container component component-file-id position {}))
|
||||
|
||||
orig-pos (gpt/point (:x component-shape) (:y component-shape))
|
||||
delta (gpt/subtract position orig-pos)
|
||||
([container component component-file-id position
|
||||
{:keys [main-instance? force-id] :or {main-instance? false force-id nil}}]
|
||||
(let [component-shape (get-shape component (:id component))
|
||||
|
||||
objects (:objects container)
|
||||
unames (volatile! (ctst/retrieve-used-names objects))
|
||||
orig-pos (gpt/point (:x component-shape) (:y component-shape))
|
||||
delta (gpt/subtract position orig-pos)
|
||||
|
||||
frame-id (ctst/frame-id-by-position objects (gpt/add orig-pos delta))
|
||||
objects (:objects container)
|
||||
unames (volatile! (ctst/retrieve-used-names objects))
|
||||
|
||||
update-new-shape
|
||||
(fn [new-shape original-shape]
|
||||
(let [new-name (ctst/generate-unique-name @unames (:name new-shape))]
|
||||
frame-id (ctst/frame-id-by-position objects (gpt/add orig-pos delta))
|
||||
|
||||
(when (nil? (:parent-id original-shape))
|
||||
(vswap! unames conj new-name))
|
||||
update-new-shape
|
||||
(fn [new-shape original-shape]
|
||||
(let [new-name (ctst/generate-unique-name @unames (:name new-shape))]
|
||||
|
||||
(cond-> new-shape
|
||||
true
|
||||
(as-> $
|
||||
(gsh/move $ delta)
|
||||
(assoc $ :frame-id frame-id)
|
||||
(assoc $ :parent-id
|
||||
(or (:parent-id $) (:frame-id $)))
|
||||
(dissoc $ :touched))
|
||||
(when (nil? (:parent-id original-shape))
|
||||
(vswap! unames conj new-name))
|
||||
|
||||
(nil? (:shape-ref original-shape))
|
||||
(assoc :shape-ref (:id original-shape))
|
||||
(cond-> new-shape
|
||||
true
|
||||
(as-> $
|
||||
(gsh/move $ delta)
|
||||
(assoc $ :frame-id frame-id)
|
||||
(assoc $ :parent-id
|
||||
(or (:parent-id $) (:frame-id $)))
|
||||
(dissoc $ :touched))
|
||||
|
||||
(nil? (:parent-id original-shape))
|
||||
(assoc :component-id (:id original-shape)
|
||||
:component-file component-file-id
|
||||
:component-root? true
|
||||
:name new-name)
|
||||
(nil? (:shape-ref original-shape))
|
||||
(assoc :shape-ref (:id original-shape))
|
||||
|
||||
(and (nil? (:parent-id original-shape)) main-instance?)
|
||||
(assoc :main-instance? true)
|
||||
(nil? (:parent-id original-shape))
|
||||
(assoc :component-id (:id original-shape)
|
||||
:component-file component-file-id
|
||||
:component-root? true
|
||||
:name new-name)
|
||||
|
||||
(some? (:parent-id original-shape))
|
||||
(dissoc :component-root?))))
|
||||
(and (nil? (:parent-id original-shape)) main-instance?)
|
||||
(assoc :main-instance? true)
|
||||
|
||||
[new-shape new-shapes _]
|
||||
(ctst/clone-object component-shape
|
||||
nil
|
||||
(get component :objects)
|
||||
update-new-shape)]
|
||||
(some? (:parent-id original-shape))
|
||||
(dissoc :component-root?))))
|
||||
|
||||
[new-shape new-shapes _]
|
||||
(ctst/clone-object component-shape
|
||||
nil
|
||||
(get component :objects)
|
||||
update-new-shape
|
||||
(fn [object _] object)
|
||||
force-id)]
|
||||
|
||||
[new-shape new-shapes])))
|
||||
|
||||
[new-shape new-shapes]))
|
||||
|
||||
|
|
|
@ -124,29 +124,32 @@
|
|||
"Delete a component and store it to be able to be recovered later.
|
||||
|
||||
Remember also the position of the main instance."
|
||||
[file-data component-id]
|
||||
(let [components-v2 (get-in file-data [:options :components-v2])
|
||||
([file-data component-id]
|
||||
(delete-component file-data component-id false))
|
||||
|
||||
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)))]
|
||||
([file-data component-id skip-undelete?]
|
||||
(let [components-v2 (get-in file-data [:options :components-v2])
|
||||
|
||||
(cond-> file-data
|
||||
components-v2
|
||||
(add-to-deleted-components)
|
||||
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)))]
|
||||
|
||||
:always
|
||||
(ctkl/delete-component component-id))))
|
||||
(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."
|
||||
|
@ -253,7 +256,7 @@
|
|||
component
|
||||
(:id file-data)
|
||||
position
|
||||
true)
|
||||
{:main-instance? true})
|
||||
|
||||
add-shapes
|
||||
(fn [page]
|
||||
|
@ -317,7 +320,7 @@
|
|||
component
|
||||
(:id file-data)
|
||||
position
|
||||
true)
|
||||
{:main-instance? true})
|
||||
|
||||
; Add all shapes of the main instance to the library page
|
||||
add-main-instance-shapes
|
||||
|
|
|
@ -284,10 +284,13 @@
|
|||
the order of the children of each parent."
|
||||
|
||||
([object parent-id objects update-new-object]
|
||||
(clone-object object parent-id objects update-new-object (fn [object _] object)))
|
||||
(clone-object object parent-id objects update-new-object (fn [object _] object) nil))
|
||||
|
||||
([object parent-id objects update-new-object update-original-object]
|
||||
(let [new-id (uuid/next)]
|
||||
(clone-object object parent-id objects update-new-object update-original-object nil))
|
||||
|
||||
([object parent-id objects update-new-object update-original-object force-id]
|
||||
(let [new-id (or force-id (uuid/next))]
|
||||
(loop [child-ids (seq (:shapes object))
|
||||
new-direct-children []
|
||||
new-children []
|
||||
|
|
|
@ -97,8 +97,7 @@
|
|||
(ctn/make-component-instance (ctpl/get-page file-data page-id)
|
||||
(ctkl/get-component (:data library) component-id)
|
||||
(:id library)
|
||||
(gpt/point 0 0)
|
||||
false)]
|
||||
(gpt/point 0 0))]
|
||||
|
||||
(swap! idmap assoc label (:id instance-shape))
|
||||
(-> file-data
|
||||
|
|
|
@ -118,8 +118,7 @@
|
|||
:name (:name new-component-shape)
|
||||
:objects (d/index-by :id new-component-shapes)}
|
||||
(:component-file main-instance-shape)
|
||||
position
|
||||
false))]
|
||||
position))]
|
||||
|
||||
[new-component-shape new-component-shapes
|
||||
new-instance-shape new-instance-shapes]))
|
||||
|
@ -130,7 +129,7 @@
|
|||
(let [component (cph/get-component libraries file-id component-id)
|
||||
|
||||
[new-shape new-shapes]
|
||||
(ctn/make-component-instance page component file-id position false)
|
||||
(ctn/make-component-instance page component file-id position)
|
||||
|
||||
changes (reduce #(pcb/add-object %1 %2 {:ignore-touched true})
|
||||
(pcb/empty-changes it (:id page))
|
||||
|
|
|
@ -220,7 +220,7 @@
|
|||
:fill "none"}
|
||||
|
||||
(when include-metadata?
|
||||
[:& export/export-page {:options (:options data)}])
|
||||
[:& export/export-page {:id (:id data) :options (:options data)}])
|
||||
|
||||
(let [shapes (->> shapes
|
||||
(remove cph/frame-shape?)
|
||||
|
@ -393,6 +393,11 @@
|
|||
object (get objects id)
|
||||
selrect (:selrect object)
|
||||
|
||||
main-instance-id (:main-instance-id data)
|
||||
main-instance-page (:main-instance-page data)
|
||||
main-instance-x (:main-instance-x data)
|
||||
main-instance-y (:main-instance-y data)
|
||||
|
||||
vbox
|
||||
(format-viewbox
|
||||
{:width (:width selrect)
|
||||
|
@ -403,7 +408,13 @@
|
|||
(mf/deps objects)
|
||||
(fn [] (group-wrapper-factory objects)))]
|
||||
|
||||
[:> "symbol" #js {:id (str id) :viewBox vbox "penpot:path" path}
|
||||
[:> "symbol" #js {:id (str id)
|
||||
:viewBox vbox
|
||||
"penpot:path" path
|
||||
"penpot:main-instance-id" main-instance-id
|
||||
"penpot:main-instance-page" main-instance-page
|
||||
"penpot:main-instance-x" main-instance-x
|
||||
"penpot:main-instance-y" main-instance-y}
|
||||
[:title name]
|
||||
[:> shape-container {:shape object}
|
||||
[:& group-wrapper {:shape object :view-box vbox}]]]))
|
||||
|
@ -414,7 +425,8 @@
|
|||
(let [data (obj/get props "data")
|
||||
children (obj/get props "children")
|
||||
render-embed? (obj/get props "render-embed?")
|
||||
include-metadata? (obj/get props "include-metadata?")]
|
||||
include-metadata? (obj/get props "include-metadata?")
|
||||
source (keyword (obj/get props "source" "components"))]
|
||||
[:& (mf/provider embed/context) {:value render-embed?}
|
||||
[:& (mf/provider export/include-metadata-ctx) {:value include-metadata?}
|
||||
[:svg {:version "1.1"
|
||||
|
@ -424,7 +436,7 @@
|
|||
:style {:display (when-not (some? children) "none")}
|
||||
:fill "none"}
|
||||
[:defs
|
||||
(for [[id data] (:components data)]
|
||||
(for [[id data] (source data)]
|
||||
[:& component-symbol {:id id :key (dm/str id) :data data}])]
|
||||
|
||||
children]]]))
|
||||
|
@ -482,9 +494,9 @@
|
|||
(rds/renderToStaticMarkup elem)))))))
|
||||
|
||||
(defn render-components
|
||||
[data]
|
||||
[data source]
|
||||
(let [;; Join all components objects into a single map
|
||||
objects (->> (:components data)
|
||||
objects (->> (source data)
|
||||
(vals)
|
||||
(map :objects)
|
||||
(reduce conj))]
|
||||
|
@ -498,5 +510,6 @@
|
|||
(rx/map
|
||||
(fn [data]
|
||||
(let [elem (mf/element components-sprite-svg
|
||||
#js {:data data :render-embed? true :include-metadata? true})]
|
||||
#js {:data data :render-embed? true :include-metadata? true
|
||||
:source (name source)})]
|
||||
(rds/renderToStaticMarkup elem))))))))
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
[app.main.data.events :as ev]
|
||||
[app.main.data.messages :as msg]
|
||||
[app.main.data.modal :as modal]
|
||||
[app.main.features :as features]
|
||||
[app.main.store :as st]
|
||||
[app.main.ui.components.file-uploader :refer [file-uploader]]
|
||||
[app.main.ui.icons :as i]
|
||||
|
@ -247,6 +248,8 @@
|
|||
:files (->> files
|
||||
(mapv #(assoc % :status :analyzing)))})
|
||||
|
||||
components-v2 (features/use-feature :components-v2)
|
||||
|
||||
analyze-import
|
||||
(mf/use-callback
|
||||
(fn [files]
|
||||
|
@ -268,6 +271,7 @@
|
|||
:num-files (count files)}))
|
||||
(->> (uw/ask-many!
|
||||
{:cmd :import-files
|
||||
:components-v2 components-v2
|
||||
:project-id project-id
|
||||
:files files})
|
||||
(rx/subs
|
||||
|
|
|
@ -54,7 +54,8 @@
|
|||
([props attr trfn]
|
||||
(let [val (get shape attr)
|
||||
val (if (keyword? val) (d/name val) val)
|
||||
ns-attr (str "penpot:" (-> attr d/name))]
|
||||
ns-attr (-> (str "penpot:" (-> attr d/name))
|
||||
(str/strip-suffix "?"))]
|
||||
(cond-> props
|
||||
(some? val)
|
||||
(obj/set! ns-attr (trfn val)))))))
|
||||
|
@ -136,7 +137,8 @@
|
|||
(add! :typography-ref-file)
|
||||
(add! :component-file)
|
||||
(add! :component-id)
|
||||
(add! :component-root)
|
||||
(add! :component-root?)
|
||||
(add! :main-instance?)
|
||||
(add! :shape-ref))))
|
||||
|
||||
(defn prefix-keys [m]
|
||||
|
@ -177,11 +179,11 @@
|
|||
:axis (d/name axis)}])])
|
||||
|
||||
(mf/defc export-page
|
||||
[{:keys [options]}]
|
||||
[{:keys [id options]}]
|
||||
(let [saved-grids (get options :saved-grids)
|
||||
flows (get options :flows)
|
||||
guides (get options :guides)]
|
||||
[:> "penpot:page" #js {}
|
||||
[:> "penpot:page" #js {:id id}
|
||||
(when (d/not-empty? saved-grids)
|
||||
(let [parse-grid (fn [[type params]] {:type type :params params})
|
||||
grids (->> saved-grids (mapv parse-grid))]
|
||||
|
|
|
@ -400,7 +400,8 @@
|
|||
component-id (get-meta node :component-id uuid/uuid)
|
||||
component-file (get-meta node :component-file uuid/uuid)
|
||||
shape-ref (get-meta node :shape-ref uuid/uuid)
|
||||
component-root? (get-meta node :component-root str->bool)]
|
||||
component-root? (get-meta node :component-root str->bool)
|
||||
main-instance? (get-meta node :main-instance str->bool)]
|
||||
|
||||
(cond-> props
|
||||
(some? stroke-color-ref-id)
|
||||
|
@ -414,6 +415,9 @@
|
|||
component-root?
|
||||
(assoc :component-root? component-root?)
|
||||
|
||||
main-instance?
|
||||
(assoc :main-instance? main-instance?)
|
||||
|
||||
(some? shape-ref)
|
||||
(assoc :shape-ref shape-ref))))
|
||||
|
||||
|
|
|
@ -40,17 +40,18 @@
|
|||
(reduce format-page {}))]
|
||||
(-> manifest
|
||||
(assoc (str (:id file))
|
||||
{:name name
|
||||
:shared is-shared
|
||||
:pages pages
|
||||
:pagesIndex index
|
||||
:version current-version
|
||||
:libraries (->> (:libraries file) (into #{}) (mapv str))
|
||||
:exportType (d/name export-type)
|
||||
:hasComponents (d/not-empty? (get-in file [:data :components]))
|
||||
:hasMedia (d/not-empty? (get-in file [:data :media]))
|
||||
:hasColors (d/not-empty? (get-in file [:data :colors]))
|
||||
:hasTypographies (d/not-empty? (get-in file [:data :typographies]))}))))]
|
||||
{:name name
|
||||
:shared is-shared
|
||||
:pages pages
|
||||
:pagesIndex index
|
||||
:version current-version
|
||||
:libraries (->> (:libraries file) (into #{}) (mapv str))
|
||||
:exportType (d/name export-type)
|
||||
:hasComponents (d/not-empty? (get-in file [:data :components]))
|
||||
: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]))
|
||||
:hasTypographies (d/not-empty? (get-in file [:data :typographies]))}))))]
|
||||
(let [manifest {:teamId (str team-id)
|
||||
:fileId (str file-id)
|
||||
:files (->> (vals files) (reduce format-file {}))}]
|
||||
|
@ -146,9 +147,14 @@
|
|||
|
||||
(defn parse-library-components
|
||||
[file]
|
||||
(->> (r/render-components (:data file))
|
||||
(->> (r/render-components (:data file) :components)
|
||||
(rx/map #(vector (str (:id file) "/components.svg") %))))
|
||||
|
||||
(defn parse-deleted-components
|
||||
[file]
|
||||
(->> (r/render-components (:data file) :deleted-components)
|
||||
(rx/map #(vector (str (:id file) "/deleted-components.svg") %))))
|
||||
|
||||
(defn fetch-file-with-libraries [file-id components-v2]
|
||||
(->> (rx/zip (rp/query :file {:id file-id :components-v2 components-v2})
|
||||
(rp/query :file-libraries {:file-id file-id}))
|
||||
|
@ -426,6 +432,12 @@
|
|||
(rx/filter #(d/not-empty? (get-in % [:data :components])))
|
||||
(rx/flat-map parse-library-components))
|
||||
|
||||
deleted-components-stream
|
||||
(->> files-stream
|
||||
(rx/flat-map vals)
|
||||
(rx/filter #(d/not-empty? (get-in % [:data :deleted-components])))
|
||||
(rx/flat-map parse-deleted-components))
|
||||
|
||||
pages-stream
|
||||
(->> render-stream
|
||||
(rx/map collect-page))]
|
||||
|
@ -441,6 +453,7 @@
|
|||
manifest-stream
|
||||
pages-stream
|
||||
components-stream
|
||||
deleted-components-stream
|
||||
media-stream
|
||||
colors-stream
|
||||
typographies-stream)
|
||||
|
|
|
@ -46,14 +46,15 @@
|
|||
([context type id media]
|
||||
(let [file-id (:file-id context)
|
||||
path (case type
|
||||
:manifest (str "manifest.json")
|
||||
:page (str file-id "/" id ".svg")
|
||||
:colors (str file-id "/colors.json")
|
||||
:typographies (str file-id "/typographies.json")
|
||||
:media-list (str file-id "/media.json")
|
||||
:media (let [ext (cm/mtype->extension (:mtype media))]
|
||||
(str/concat file-id "/media/" id ext))
|
||||
:components (str file-id "/components.svg"))
|
||||
:manifest (str "manifest.json")
|
||||
:page (str file-id "/" id ".svg")
|
||||
:colors (str file-id "/colors.json")
|
||||
:typographies (str file-id "/typographies.json")
|
||||
:media-list (str file-id "/media.json")
|
||||
:media (let [ext (cm/mtype->extension (:mtype media))]
|
||||
(str/concat file-id "/media/" id ext))
|
||||
:components (str file-id "/components.svg")
|
||||
:deleted-components (str file-id "/deleted-components.svg"))
|
||||
|
||||
parse-svg? (and (not= type :media) (str/ends-with? path "svg"))
|
||||
parse-json? (and (not= type :media) (str/ends-with? path "json"))
|
||||
|
@ -125,7 +126,7 @@
|
|||
|
||||
(defn create-file
|
||||
"Create a new file on the back-end"
|
||||
[context]
|
||||
[context components-v2]
|
||||
(let [resolve (:resolve context)
|
||||
file-id (resolve (:file-id context))]
|
||||
(rp/mutation :create-temp-file
|
||||
|
@ -133,7 +134,9 @@
|
|||
:name (:name context)
|
||||
:is-shared (:shared context)
|
||||
:project-id (:project-id context)
|
||||
:data (-> ctf/empty-file-data (assoc :id file-id))})))
|
||||
:data (-> ctf/empty-file-data
|
||||
(assoc :id file-id)
|
||||
(assoc-in [:options :components-v2] components-v2))})))
|
||||
|
||||
(defn link-file-libraries
|
||||
"Create a new file on the back-end"
|
||||
|
@ -380,18 +383,22 @@
|
|||
(rx/map (comp fb/close-page setup-interactions))))))))
|
||||
|
||||
(defn import-component [context file node]
|
||||
(let [resolve (:resolve context)
|
||||
content (cip/find-node node :g)
|
||||
file-id (:id file)
|
||||
old-id (cip/get-id node)
|
||||
id (resolve old-id)
|
||||
path (get-in node [:attrs :penpot:path] "")
|
||||
data (-> (cip/parse-data :group content)
|
||||
(assoc :path path)
|
||||
(assoc :id id))
|
||||
(let [resolve (:resolve context)
|
||||
content (cip/find-node node :g)
|
||||
file-id (:id file)
|
||||
old-id (cip/get-id node)
|
||||
id (resolve old-id)
|
||||
path (get-in node [:attrs :penpot:path] "")
|
||||
main-instance-id (resolve (uuid (get-in node [:attrs :penpot:main-instance-id] "")))
|
||||
main-instance-page (resolve (uuid (get-in node [:attrs :penpot:main-instance-page] "")))
|
||||
data (-> (cip/parse-data :group content)
|
||||
(assoc :path path)
|
||||
(assoc :id id)
|
||||
(assoc :main-instance-id main-instance-id)
|
||||
(assoc :main-instance-page main-instance-page))
|
||||
|
||||
file (-> file (fb/start-component data))
|
||||
children (cip/node-seq node)]
|
||||
file (-> file (fb/start-component data))
|
||||
children (cip/node-seq node)]
|
||||
|
||||
(->> (rx/from children)
|
||||
(rx/filter cip/shape?)
|
||||
|
@ -401,6 +408,43 @@
|
|||
(rx/reduce (partial process-import-node context) file)
|
||||
(rx/map fb/finish-component))))
|
||||
|
||||
(defn import-deleted-component [context file node]
|
||||
(let [resolve (:resolve context)
|
||||
content (cip/find-node node :g)
|
||||
file-id (:id file)
|
||||
old-id (cip/get-id node)
|
||||
id (resolve old-id)
|
||||
path (get-in node [:attrs :penpot:path] "")
|
||||
main-instance-id (resolve (uuid (get-in node [:attrs :penpot:main-instance-id] "")))
|
||||
main-instance-page (resolve (uuid (get-in node [:attrs :penpot:main-instance-page] "")))
|
||||
main-instance-x (get-in node [:attrs :penpot:main-instance-x] "")
|
||||
main-instance-y (get-in node [:attrs :penpot:main-instance-y] "")
|
||||
|
||||
data (-> (cip/parse-data :group content)
|
||||
(assoc :path path)
|
||||
(assoc :id id)
|
||||
(assoc :main-instance-id main-instance-id)
|
||||
(assoc :main-instance-page main-instance-page)
|
||||
(assoc :main-instance-x main-instance-x)
|
||||
(assoc :main-instance-y main-instance-y))
|
||||
|
||||
file (-> file (fb/start-component data))
|
||||
component-id (:current-component-id file)
|
||||
children (cip/node-seq node)]
|
||||
|
||||
(->> (rx/from children)
|
||||
(rx/filter cip/shape?)
|
||||
(rx/skip 1)
|
||||
(rx/skip-last 1)
|
||||
(rx/mapcat (partial resolve-media context file-id))
|
||||
(rx/reduce (partial process-import-node context) file)
|
||||
(rx/map fb/finish-component)
|
||||
(rx/map (partial fb/finish-deleted-component
|
||||
component-id
|
||||
main-instance-page
|
||||
main-instance-x
|
||||
main-instance-y)))))
|
||||
|
||||
(defn process-pages
|
||||
[context file]
|
||||
(let [index (:pages-index context)
|
||||
|
@ -486,6 +530,18 @@
|
|||
(rx/concat-reduce (partial import-component context) file)))
|
||||
(rx/of file)))
|
||||
|
||||
(defn process-deleted-components
|
||||
[context file]
|
||||
(if (:has-deleted-components context)
|
||||
(let [split-components
|
||||
(fn [content] (->> (cip/node-seq content)
|
||||
(filter #(= :symbol (:tag %)))))]
|
||||
|
||||
(->> (get-file context :deleted-components)
|
||||
(rx/flat-map split-components)
|
||||
(rx/concat-reduce (partial import-deleted-component context) file)))
|
||||
(rx/of file)))
|
||||
|
||||
(defn process-file
|
||||
[context file]
|
||||
|
||||
|
@ -502,18 +558,20 @@
|
|||
(rx/flat-map (partial process-library-media context))
|
||||
(rx/tap #(progress! context :process-components))
|
||||
(rx/flat-map (partial process-library-components context))
|
||||
(rx/tap #(progress! context :process-deleted-components))
|
||||
(rx/flat-map (partial process-deleted-components context))
|
||||
(rx/flat-map (partial send-changes context))
|
||||
(rx/tap #(rx/end! progress-str)))]))
|
||||
|
||||
(defn create-files
|
||||
[context files]
|
||||
[context files components-v2]
|
||||
|
||||
(let [data (group-by :file-id files)]
|
||||
(rx/concat
|
||||
(->> (rx/from files)
|
||||
(rx/map #(merge context %))
|
||||
(rx/flat-map (fn [context]
|
||||
(->> (create-file context)
|
||||
(->> (create-file context components-v2)
|
||||
(rx/map #(vector % (first (get data (:file-id context)))))))))
|
||||
|
||||
(->> (rx/from files)
|
||||
|
@ -564,7 +622,7 @@
|
|||
(rx/catch #(rx/of {:uri (:uri file) :error (.-message %)}))))))))
|
||||
|
||||
(defmethod impl/handler :import-files
|
||||
[{:keys [project-id files]}]
|
||||
[{:keys [project-id files components-v2]}]
|
||||
|
||||
(let [context {:project-id project-id
|
||||
:resolve (resolve-factory)}
|
||||
|
@ -572,7 +630,7 @@
|
|||
binary-files (filter #(= "application/octet-stream" (:type %)) files)]
|
||||
|
||||
(->> (rx/merge
|
||||
(->> (create-files context zip-files)
|
||||
(->> (create-files context zip-files components-v2)
|
||||
(rx/flat-map
|
||||
(fn [[file data]]
|
||||
(->> (uz/load-from-url (:uri data))
|
||||
|
|
Loading…
Add table
Reference in a new issue