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:
parent
234a698538
commit
9b9959da9a
5 changed files with 183 additions and 44 deletions
|
@ -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]]))
|
||||||
|
|
|
@ -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)))))))
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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)]]))])])))
|
||||||
|
|
||||||
|
|
|
@ -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)
|
||||||
|
|
Loading…
Add table
Reference in a new issue