🎉 Import & export new components

This commit is contained in:
Andrés Moya 2022-08-31 17:48:02 +02:00
parent 251e7eada2
commit 46053b6bbf
15 changed files with 283 additions and 122 deletions

View file

@ -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))

View file

@ -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))))))))

View file

@ -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

View file

@ -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))]

View file

@ -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))))

View file

@ -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)

View file

@ -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))