0
Fork 0
mirror of https://github.com/penpot/penpot.git synced 2025-01-26 08:29:42 -05:00

Export library components

This commit is contained in:
alonso.torres 2021-06-22 09:58:18 +02:00
parent 234a698538
commit 9b9959da9a
5 changed files with 183 additions and 44 deletions

View file

@ -25,6 +25,7 @@
[app.main.ui.shapes.shape :refer [shape-container]] [app.main.ui.shapes.shape :refer [shape-container]]
[app.main.ui.shapes.svg-raw :as svg-raw] [app.main.ui.shapes.svg-raw :as svg-raw]
[app.main.ui.shapes.text :as text] [app.main.ui.shapes.text :as text]
[app.util.object :as obj]
[app.util.timers :as ts] [app.util.timers :as ts]
[cuerdas.core :as str] [cuerdas.core :as str]
[rumext.alpha :as mf])) [rumext.alpha :as mf]))
@ -73,10 +74,11 @@
(mf/fnc group-wrapper (mf/fnc group-wrapper
[{:keys [shape frame] :as props}] [{:keys [shape frame] :as props}]
(let [childs (mapv #(get objects %) (:shapes shape))] (let [childs (mapv #(get objects %) (:shapes shape))]
[:& group-shape {:frame frame [:& shape-container {:shape shape}
:shape shape [:& group-shape {:frame frame
:is-child-selected? true :shape shape
:childs childs}])))) :is-child-selected? true
:childs childs}]]))))
(defn svg-raw-wrapper-factory (defn svg-raw-wrapper-factory
[objects] [objects]
@ -207,7 +209,8 @@
:height height :height height
:version "1.1" :version "1.1"
:xmlnsXlink "http://www.w3.org/1999/xlink" :xmlnsXlink "http://www.w3.org/1999/xlink"
:xmlns "http://www.w3.org/2000/svg"} :xmlns "http://www.w3.org/2000/svg"
:xmlns:penpot "https://penpot.app/xmlns"}
[:& wrapper {:shape frame :view-box vbox}]])) [:& wrapper {:shape frame :view-box vbox}]]))
(mf/defc component-svg (mf/defc component-svg
@ -238,5 +241,58 @@
:height height :height height
:version "1.1" :version "1.1"
:xmlnsXlink "http://www.w3.org/1999/xlink" :xmlnsXlink "http://www.w3.org/1999/xlink"
:xmlns "http://www.w3.org/2000/svg"} :xmlns "http://www.w3.org/2000/svg"
:xmlns:penpot "https://penpot.app/xmlns"}
[:& wrapper {:shape group :view-box vbox}]])) [:& wrapper {:shape group :view-box vbox}]]))
(mf/defc component-symbol
[{:keys [id data] :as props}]
(let [{:keys [name objects]} data
root (get objects id)
{:keys [width height]} (:selrect root)
vbox (str "0 0 " width " " height)
modifier (-> (gpt/point (:x root) (:y root))
(gpt/negate)
(gmt/translate-matrix))
modifier-ids (concat [id] (cp/get-children id objects))
update-fn #(assoc-in %1 [%2 :modifiers :displacement] modifier)
objects (reduce update-fn objects modifier-ids)
root (assoc-in root [:modifiers :displacement] modifier)
group-wrapper
(mf/use-memo
(mf/deps objects)
#(group-wrapper-factory objects))]
[:symbol {:id (str id)
:viewBox vbox}
[:title name]
[:& group-wrapper {:shape root :view-box vbox}]]))
(mf/defc components-sprite-svg
{::mf/wrap-props false}
[props]
(let [data (obj/get props "data")
children (obj/get props "children")
embed? (obj/get props "embed?")]
[:& (mf/provider embed/context) {:value embed?}
[:svg {:version "1.1"
:xmlns "http://www.w3.org/2000/svg"
:xmlnsXlink "http://www.w3.org/1999/xlink"
:xmlns:penpot "https://penpot.app/xmlns"
:style {:width "100vw"
:height "100vh"
:display (when-not (some? children) "none")}}
[:defs
(for [[component-id component-data] (:components data)]
[:& component-symbol {:id component-id
:key (str component-id)
:data component-data}])]
children]]))

View file

@ -66,5 +66,16 @@
(let [elem (mf/element exports/page-svg #js {:data data :embed? true})] (let [elem (mf/element exports/page-svg #js {:data data :embed? true})]
(rds/renderToStaticMarkup elem))))))) (rds/renderToStaticMarkup elem)))))))
(defn render-components
[data]
(rx/concat
(->> (rx/merge
(populate-images-cache data)
(populate-fonts-cache data))
(rx/ignore))
(->> (rx/of data)
(rx/map
(fn [data]
(let [elem (mf/element exports/components-sprite-svg #js {:data data :embed? true})]
(rds/renderToStaticMarkup elem)))))))

View file

@ -82,6 +82,7 @@
;; Used for export ;; Used for export
["/render-object/:file-id/:page-id/:object-id" :render-object] ["/render-object/:file-id/:page-id/:object-id" :render-object]
["/render-sprite/:file-id" :render-sprite]
["/dashboard/team/:team-id" ["/dashboard/team/:team-id"
["/members" :dashboard-team-members] ["/members" :dashboard-team-members]
@ -172,6 +173,14 @@
:page-id page-id :page-id page-id
:object-id object-id}])) :object-id object-id}]))
:render-sprite
(do
(let [file-id (uuid (get-in route [:path-params :file-id]))
component-id (get-in route [:query-params :component-id])
component-id (when (some? component-id) (uuid component-id))]
[:& render/render-sprite {:file-id file-id
:component-id component-id}]))
:workspace :workspace
(let [project-id (some-> params :path :project-id uuid) (let [project-id (some-> params :path :project-id uuid)
file-id (some-> params :path :file-id uuid) file-id (some-> params :path :file-id uuid)

View file

@ -123,3 +123,30 @@
[:& object-svg {:objects @objects [:& object-svg {:objects @objects
:object-id object-id :object-id object-id
:zoom 1}]))) :zoom 1}])))
(mf/defc render-sprite
[{:keys [file-id component-id] :as props}]
(let [file (mf/use-state nil)]
(mf/use-effect
(mf/deps file-id)
(fn []
(->> (repo/query! :file {:id file-id})
(rx/subs
(fn [result]
(reset! file result))))
(constantly nil)))
(when @file
[:*
[:& exports/components-sprite-svg {:data (:data @file) :embed true}
(when (some? component-id)
[:use {:x 0 :y 0
:xlinkHref (str "#" component-id)}])]
(when-not (some? component-id)
[:ul
(for [[id data] (get-in @file [:data :components])]
(let [url (str "#/render-sprite/" (:id @file) "?component-id=" id)]
[:li [:a {:href url} (:name data)]]))])])))

View file

@ -70,26 +70,77 @@
[{:keys [id file-id markup] :as page}] [{:keys [id file-id markup] :as page}]
[(str file-id "/" id ".svg") markup]) [(str file-id "/" id ".svg") markup])
(defn collect-color (defn collect-entries [result data keys]
[result color]
(-> result (-> result
(assoc (str (:id color)) (assoc (str (:id data))
(->> (select-keys color [:name :color :opacity :gradient]) (->> (select-keys data keys)
(d/deep-mapm (d/deep-mapm
(fn [[k v]] (fn [[k v]]
[(-> k str/camel) v])))))) [(-> k str/camel) v]))))))
(def ^:const typography-keys [:name :font-family :font-id :font-size (def ^:const color-keys
:font-style :font-variant-id :font-weight [:name :color :opacity :gradient])
:letter-spacing :line-height :text-transform])
(def ^:const typography-keys
[:name :font-family :font-id :font-size :font-style :font-variant-id :font-weight
:letter-spacing :line-height :text-transform])
(def ^:const media-keys
[:name :mtype :width :height])
(defn collect-color
[result color]
(collect-entries result color color-keys))
(defn collect-typography (defn collect-typography
[result typography] [result typography]
(-> result (collect-entries result typography typography-keys))
(assoc (str (:id typography))
(->> (select-keys typography typography-keys) (defn collect-media
(d/deep-mapm [result media]
(fn [[k v]] (collect-entries result media media-keys))
[(-> k str/camel) v]))))))
(defn parse-library-color
[[file-id colors]]
(let [markup
(->> (vals colors)
(reduce collect-color {})
(json/encode))]
[(str file-id "/colors.json") markup]))
(defn parse-library-typographies
[[file-id typographies]]
(let [markup
(->> (vals typographies)
(reduce collect-typography {})
(json/encode))]
[(str file-id "/typographies.json") markup]))
(defn parse-library-media
[[file-id media]]
(rx/merge
(let [markup
(->> (vals media)
(reduce collect-media {})
(json/encode))]
(rx/of (vector (str file-id "/images.json") markup)))
(->> (rx/from (vals media))
(rx/map #(assoc % :file-id file-id))
(rx/flat-map
(fn [media]
(let [file-path (str file-id "/images/" (:id media) "." (dom/mtype->extension (:mtype media)))]
(->> (http/send!
{:uri (cfg/resolve-file-media media)
:response-type :blob
:method :get})
(rx/map :body)
(rx/map #(vector file-path %)))))))))
(defn parse-library-components
[file]
(->> (r/render-components (:data file))
(rx/map #(vector (str (:id file) "/components.svg") %))))
(defn export-file (defn export-file
[team-id file-id] [team-id file-id]
@ -120,42 +171,27 @@
(rx/flat-map vals) (rx/flat-map vals)
(rx/map #(vector (:id %) (get-in % [:data :colors]))) (rx/map #(vector (:id %) (get-in % [:data :colors])))
(rx/filter #(d/not-empty? (second %))) (rx/filter #(d/not-empty? (second %)))
(rx/map (fn [[file-id colors]] (rx/map parse-library-color))
(let [markup
(->> (vals colors)
(reduce collect-color {})
(json/encode))]
[(str file-id "/colors.json") markup]))))
typographies-stream typographies-stream
(->> files-stream (->> files-stream
(rx/flat-map vals) (rx/flat-map vals)
(rx/map #(vector (:id %) (get-in % [:data :typographies]))) (rx/map #(vector (:id %) (get-in % [:data :typographies])))
(rx/filter #(d/not-empty? (second %))) (rx/filter #(d/not-empty? (second %)))
(rx/map (fn [[file-id typographies]] (rx/map parse-library-typographies))
(let [markup
(->> (vals typographies)
(reduce collect-typography {})
(json/encode))]
[(str file-id "/typographies.json") markup]))))
media-stream media-stream
(->> files-stream (->> files-stream
(rx/flat-map vals) (rx/flat-map vals)
(rx/map #(vector (:id %) (get-in % [:data :media]))) (rx/map #(vector (:id %) (get-in % [:data :media])))
(rx/filter #(d/not-empty? (second %))) (rx/filter #(d/not-empty? (second %)))
(rx/flat-map (rx/flat-map parse-library-media))
(fn [[file-id media]]
(->> media vals (mapv #(assoc % :file-id file-id)))))
(rx/flat-map components-stream
(fn [media] (->> files-stream
(->> (http/send! (rx/flat-map vals)
{:uri (cfg/resolve-file-media media) (rx/filter #(d/not-empty? (get-in % [:data :components])))
:response-type :blob (rx/flat-map parse-library-components))
:method :get})
(rx/map :body)
(rx/map #(vector (str file-id "/images/" (:id media)) %))))))
pages-stream pages-stream
(->> render-stream (->> render-stream
@ -171,7 +207,7 @@
(->> (rx/merge (->> (rx/merge
manifest-stream manifest-stream
pages-stream pages-stream
#_components-stream components-stream
media-stream media-stream
colors-stream colors-stream
typographies-stream) typographies-stream)