mirror of
https://github.com/penpot/penpot.git
synced 2025-04-12 23:11:23 -05:00
Merge pull request #1053 from penpot/feat/export-import
Feat/export import
This commit is contained in:
commit
be387ad892
16 changed files with 503 additions and 238 deletions
|
@ -17,14 +17,7 @@
|
|||
{:exclude-files
|
||||
["data_readers.clj"
|
||||
"app/util/perf.cljs"
|
||||
"app/common/exceptions.cljc"
|
||||
"app/util/import/.*"
|
||||
"app/worker/export.cljs"
|
||||
"app/worker/import.cljs"
|
||||
"app/libs/.*"
|
||||
"app/main/data/workspace/path/selection.cljs"
|
||||
"app/main/data/workspace/transforms.cljs"
|
||||
]}
|
||||
"app/common/exceptions.cljc"]}
|
||||
|
||||
:linters
|
||||
{:unsorted-required-namespaces
|
||||
|
|
|
@ -556,3 +556,7 @@
|
|||
|
||||
:else
|
||||
m)))
|
||||
|
||||
(defn not-empty?
|
||||
[coll]
|
||||
(boolean (seq coll)))
|
||||
|
|
|
@ -291,6 +291,31 @@
|
|||
(-> file
|
||||
(update :parent-stack pop)))
|
||||
|
||||
(defn add-interaction
|
||||
[file action-type event-type from-id destination-id]
|
||||
|
||||
(assert (some? (lookup-shape file from-id)) (str "Cannot locate shape with id " from-id))
|
||||
(assert (some? (lookup-shape file destination-id)) (str "Cannot locate shape with id " destination-id))
|
||||
|
||||
(let [interactions (->> (lookup-shape file from-id)
|
||||
:interactions
|
||||
(filterv #(or (not= (:action-type %) action-type)
|
||||
(not= (:event-type %) event-type))))
|
||||
conj (fnil conj [])
|
||||
interactions (-> interactions
|
||||
(conj
|
||||
{:action-type action-type
|
||||
:event-type event-type
|
||||
:destination destination-id}))]
|
||||
(commit-change
|
||||
file
|
||||
{:type :mod-obj
|
||||
:page-id (:current-page-id file)
|
||||
:id from-id
|
||||
|
||||
:operations
|
||||
[{:type :set :attr :interactions :val interactions}]})))
|
||||
|
||||
(defn generate-changes
|
||||
[file]
|
||||
(:changes file))
|
||||
|
|
|
@ -25,59 +25,59 @@
|
|||
(deftype File [^:mutable file]
|
||||
Object
|
||||
|
||||
(addPage [self name]
|
||||
(addPage [_ name]
|
||||
(set! file (fb/add-page file {:name name}))
|
||||
(str (:current-page-id file)))
|
||||
|
||||
(addPage [self name options]
|
||||
(addPage [_ name options]
|
||||
(set! file (fb/add-page file {:name name :options options}))
|
||||
(str (:current-page-id file)))
|
||||
|
||||
(closePage [self]
|
||||
(closePage [_]
|
||||
(set! file (fb/close-page file)))
|
||||
|
||||
(addArtboard [self data]
|
||||
(addArtboard [_ data]
|
||||
(set! file (fb/add-artboard file (parse-data data)))
|
||||
(str (:last-id file)))
|
||||
|
||||
(closeArtboard [self data]
|
||||
(closeArtboard [_]
|
||||
(set! file (fb/close-artboard file)))
|
||||
|
||||
(addGroup [self data]
|
||||
(addGroup [_ data]
|
||||
(set! file (fb/add-group file (parse-data data)))
|
||||
(str (:last-id file)))
|
||||
|
||||
(closeGroup [self]
|
||||
(closeGroup [_]
|
||||
(set! file (fb/close-group file)))
|
||||
|
||||
(createRect [self data]
|
||||
(createRect [_ data]
|
||||
(set! file (fb/create-rect file (parse-data data)))
|
||||
(str (:last-id file)))
|
||||
|
||||
(createCircle [self data]
|
||||
(createCircle [_ data]
|
||||
(set! file (fb/create-circle file (parse-data data)))
|
||||
(str (:last-id file)))
|
||||
|
||||
(createPath [self data]
|
||||
(createPath [_ data]
|
||||
(set! file (fb/create-path file (parse-data data)))
|
||||
(str (:last-id file)))
|
||||
|
||||
(createText [self data]
|
||||
(createText [_ data]
|
||||
(set! file (fb/create-text file (parse-data data)))
|
||||
(str (:last-id file)))
|
||||
|
||||
(createImage [self data]
|
||||
(createImage [_ data]
|
||||
(set! file (fb/create-image file (parse-data data)))
|
||||
(str (:last-id file)))
|
||||
|
||||
(createSVG [self data]
|
||||
(createSVG [_ data]
|
||||
(set! file (fb/create-svg-raw file (parse-data data)))
|
||||
(str (:last-id file)))
|
||||
|
||||
(closeSVG [self]
|
||||
(closeSVG [_]
|
||||
(set! file (fb/close-svg-raw file)))
|
||||
|
||||
(asMap [self]
|
||||
(asMap [_]
|
||||
(clj->js file)))
|
||||
|
||||
(defn create-file-export [^string name]
|
||||
|
|
|
@ -141,9 +141,9 @@
|
|||
selected? (contains? selected-points position)]
|
||||
(streams/drag-stream
|
||||
(rx/of
|
||||
(when-not selected? (selection/select-node position shift? "drag"))
|
||||
(when-not selected? (selection/select-node position shift?))
|
||||
(drag-selected-points @ms/mouse-position))
|
||||
(rx/of (selection/select-node position shift? "click")))))))
|
||||
(rx/of (selection/select-node position shift?)))))))
|
||||
|
||||
(defn drag-selected-points
|
||||
[start-position]
|
||||
|
|
|
@ -60,7 +60,7 @@
|
|||
(some? id)
|
||||
(assoc-in [:workspace-local :edit-path id :selected-points] positions))))))
|
||||
|
||||
(defn select-node [position shift? kk]
|
||||
(defn select-node [position shift?]
|
||||
(ptk/reify ::select-node
|
||||
ptk/UpdateEvent
|
||||
(update [_ state]
|
||||
|
@ -79,38 +79,6 @@
|
|||
(some? id)
|
||||
(assoc-in [:workspace-local :edit-path id :selected-points] selected-points))))))
|
||||
|
||||
(defn deselect-node [position shift?]
|
||||
(ptk/reify ::deselect-node
|
||||
ptk/UpdateEvent
|
||||
(update [_ state]
|
||||
(let [id (get-in state [:workspace-local :edition])]
|
||||
(-> state
|
||||
(update-in [:workspace-local :edit-path id :selected-points] (fnil disj #{}) position))))))
|
||||
|
||||
(defn add-to-selection-handler [index type]
|
||||
(ptk/reify ::add-to-selection-handler
|
||||
ptk/UpdateEvent
|
||||
(update [_ state]
|
||||
state)))
|
||||
|
||||
(defn add-to-selection-node [index]
|
||||
(ptk/reify ::add-to-selection-node
|
||||
ptk/UpdateEvent
|
||||
(update [_ state]
|
||||
state)))
|
||||
|
||||
(defn remove-from-selection-handler [index]
|
||||
(ptk/reify ::remove-from-selection-handler
|
||||
ptk/UpdateEvent
|
||||
(update [_ state]
|
||||
state)))
|
||||
|
||||
(defn remove-from-selection-node [index]
|
||||
(ptk/reify ::remove-from-selection-handler
|
||||
ptk/UpdateEvent
|
||||
(update [_ state]
|
||||
state)))
|
||||
|
||||
(defn deselect-all []
|
||||
(ptk/reify ::deselect-all
|
||||
ptk/UpdateEvent
|
||||
|
@ -140,7 +108,7 @@
|
|||
|
||||
(ptk/reify ::handle-selection
|
||||
ptk/WatchEvent
|
||||
(watch [_ state stream]
|
||||
(watch [_ _ stream]
|
||||
(let [stop? (fn [event] (or (dwc/interrupt? event) (ms/mouse-up? event)))
|
||||
stoper (->> stream (rx/filter stop?))
|
||||
from-p @ms/mouse-position]
|
||||
|
|
|
@ -98,8 +98,8 @@
|
|||
|
||||
;; -- RESIZE
|
||||
(defn start-resize
|
||||
[handler initial ids shape]
|
||||
(letfn [(resize [shape initial resizing-shapes layout [point lock? point-snap]]
|
||||
[handler ids shape]
|
||||
(letfn [(resize [shape initial layout [point lock? point-snap]]
|
||||
(let [{:keys [width height]} (:selrect shape)
|
||||
{:keys [rotation]} shape
|
||||
rotation (or rotation 0)
|
||||
|
@ -181,7 +181,7 @@
|
|||
(rx/switch-map (fn [[point :as current]]
|
||||
(->> (snap/closest-snap-point page-id resizing-shapes layout zoom point)
|
||||
(rx/map #(conj current %)))))
|
||||
(rx/mapcat (partial resize shape initial-position resizing-shapes layout))
|
||||
(rx/mapcat (partial resize shape initial-position layout))
|
||||
(rx/take-until stoper))
|
||||
(rx/of (apply-modifiers ids)
|
||||
(finish-transform))))))))
|
||||
|
@ -196,7 +196,7 @@
|
|||
(assoc-in [:workspace-local :transform] :rotate)))
|
||||
|
||||
ptk/WatchEvent
|
||||
(watch [it state stream]
|
||||
(watch [_ _ stream]
|
||||
(let [stoper (rx/filter ms/mouse-up? stream)
|
||||
group (gsh/selection-rect shapes)
|
||||
group-center (gsh/center-selrect group)
|
||||
|
@ -235,7 +235,7 @@
|
|||
[]
|
||||
(ptk/reify ::start-move-selected
|
||||
ptk/WatchEvent
|
||||
(watch [it state stream]
|
||||
(watch [_ state stream]
|
||||
(let [initial (deref ms/mouse-position)
|
||||
selected (wsh/lookup-selected state {:omit-blocked? true})
|
||||
stopper (rx/filter ms/mouse-up? stream)]
|
||||
|
@ -259,7 +259,7 @@
|
|||
(defn start-move-duplicate [from-position]
|
||||
(ptk/reify ::start-move-selected
|
||||
ptk/WatchEvent
|
||||
(watch [it state stream]
|
||||
(watch [_ _ stream]
|
||||
(->> stream
|
||||
(rx/filter (ptk/type? ::dws/duplicate-selected))
|
||||
(rx/first)
|
||||
|
@ -268,7 +268,7 @@
|
|||
(defn calculate-frame-for-move [ids]
|
||||
(ptk/reify ::calculate-frame-for-move
|
||||
ptk/WatchEvent
|
||||
(watch [it state stream]
|
||||
(watch [it state _]
|
||||
(let [position @ms/mouse-position
|
||||
page-id (:current-page-id state)
|
||||
objects (wsh/lookup-page-objects state page-id)
|
||||
|
@ -313,7 +313,7 @@
|
|||
(assoc-in [:workspace-local :transform] :move)))
|
||||
|
||||
ptk/WatchEvent
|
||||
(watch [it state stream]
|
||||
(watch [_ state stream]
|
||||
(let [page-id (:current-page-id state)
|
||||
objects (wsh/lookup-page-objects state page-id)
|
||||
selected (wsh/lookup-selected state {:omit-blocked? true})
|
||||
|
@ -349,20 +349,6 @@
|
|||
(calculate-frame-for-move ids)
|
||||
(finish-transform)))))))))
|
||||
|
||||
(defn- get-displacement-with-grid
|
||||
"Retrieve the correct displacement delta point for the
|
||||
provided direction speed and distances thresholds."
|
||||
[shape direction options]
|
||||
(let [grid-x (:grid-x options 10)
|
||||
grid-y (:grid-y options 10)
|
||||
x-mod (mod (:x shape) grid-x)
|
||||
y-mod (mod (:y shape) grid-y)]
|
||||
(case direction
|
||||
:up (gpt/point 0 (- (if (zero? y-mod) grid-y y-mod)))
|
||||
:down (gpt/point 0 (- grid-y y-mod))
|
||||
:left (gpt/point (- (if (zero? x-mod) grid-x x-mod)) 0)
|
||||
:right (gpt/point (- grid-x x-mod) 0))))
|
||||
|
||||
(defn- get-displacement
|
||||
"Retrieve the correct displacement delta point for the
|
||||
provided direction speed and distances thresholds."
|
||||
|
@ -394,7 +380,7 @@
|
|||
state))
|
||||
|
||||
ptk/WatchEvent
|
||||
(watch [it state stream]
|
||||
(watch [_ state stream]
|
||||
(if (= same-event (get-in state [:workspace-local :current-move-selected]))
|
||||
(let [selected (wsh/lookup-selected state {:omit-blocked? true})
|
||||
move-events (->> stream
|
||||
|
@ -539,7 +525,7 @@
|
|||
(defn increase-rotation [ids rotation]
|
||||
(ptk/reify ::increase-rotation
|
||||
ptk/WatchEvent
|
||||
(watch [it state stream]
|
||||
(watch [_ state _]
|
||||
|
||||
(let [page-id (:current-page-id state)
|
||||
objects (wsh/lookup-page-objects state page-id)
|
||||
|
@ -559,7 +545,7 @@
|
|||
(us/verify (s/coll-of uuid?) ids)
|
||||
(ptk/reify ::apply-modifiers
|
||||
ptk/WatchEvent
|
||||
(watch [it state stream]
|
||||
(watch [_ state _]
|
||||
(let [objects (wsh/lookup-page-objects state)
|
||||
children-ids (->> ids (mapcat #(cp/get-children % objects)))
|
||||
ids-with-children (d/concat [] children-ids ids)
|
||||
|
@ -610,11 +596,6 @@
|
|||
(update [_ state]
|
||||
|
||||
(let [page-id (:current-page-id state)
|
||||
objects (wsh/lookup-page-objects state page-id)
|
||||
|
||||
update-children
|
||||
(fn [objects ids modifiers]
|
||||
(reduce #(assoc-in %1 [%2 :modifiers] modifiers) objects ids))
|
||||
|
||||
;; For each shape updates the modifiers given as arguments
|
||||
update-shape
|
||||
|
@ -629,7 +610,7 @@
|
|||
#(reduce update-shape % ids))))
|
||||
|
||||
ptk/WatchEvent
|
||||
(watch [it state stream]
|
||||
(watch [_ state _]
|
||||
(let [page-id (:current-page-id state)
|
||||
objects (wsh/lookup-page-objects state page-id)
|
||||
ids (d/concat [] ids (mapcat #(cp/get-children % objects) ids))]
|
||||
|
@ -638,7 +619,7 @@
|
|||
(defn flip-horizontal-selected []
|
||||
(ptk/reify ::flip-horizontal-selected
|
||||
ptk/WatchEvent
|
||||
(watch [it state stream]
|
||||
(watch [_ state _]
|
||||
(let [objects (wsh/lookup-page-objects state)
|
||||
selected (wsh/lookup-selected state {:omit-blocked? true})
|
||||
shapes (map #(get objects %) selected)
|
||||
|
@ -654,7 +635,7 @@
|
|||
(defn flip-vertical-selected []
|
||||
(ptk/reify ::flip-vertical-selected
|
||||
ptk/WatchEvent
|
||||
(watch [it state stream]
|
||||
(watch [_ state _]
|
||||
(let [objects (wsh/lookup-page-objects state)
|
||||
selected (wsh/lookup-selected state {:omit-blocked? true})
|
||||
shapes (map #(get objects %) selected)
|
||||
|
@ -687,6 +668,6 @@
|
|||
[]
|
||||
(ptk/reify ::selected-to-path
|
||||
ptk/WatchEvent
|
||||
(watch [_ state stream]
|
||||
(watch [_ state _]
|
||||
(let [ids (wsh/lookup-selected state {:omit-blocked? true})]
|
||||
(rx/of (dch/update-shapes ids ups/convert-to-path))))))
|
||||
|
|
|
@ -25,6 +25,7 @@
|
|||
[app.main.ui.shapes.shape :refer [shape-container]]
|
||||
[app.main.ui.shapes.svg-raw :as svg-raw]
|
||||
[app.main.ui.shapes.text :as text]
|
||||
[app.util.object :as obj]
|
||||
[app.util.timers :as ts]
|
||||
[cuerdas.core :as str]
|
||||
[rumext.alpha :as mf]))
|
||||
|
@ -73,10 +74,11 @@
|
|||
(mf/fnc group-wrapper
|
||||
[{:keys [shape frame] :as props}]
|
||||
(let [childs (mapv #(get objects %) (:shapes shape))]
|
||||
[:& group-shape {:frame frame
|
||||
:shape shape
|
||||
:is-child-selected? true
|
||||
:childs childs}]))))
|
||||
[:& shape-container {:shape shape}
|
||||
[:& group-shape {:frame frame
|
||||
:shape shape
|
||||
:is-child-selected? true
|
||||
:childs childs}]]))))
|
||||
|
||||
(defn svg-raw-wrapper-factory
|
||||
[objects]
|
||||
|
@ -207,7 +209,8 @@
|
|||
:height height
|
||||
:version "1.1"
|
||||
: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}]]))
|
||||
|
||||
(mf/defc component-svg
|
||||
|
@ -238,5 +241,58 @@
|
|||
:height height
|
||||
:version "1.1"
|
||||
: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}]]))
|
||||
|
||||
(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})]
|
||||
(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
|
||||
["/render-object/:file-id/:page-id/:object-id" :render-object]
|
||||
["/render-sprite/:file-id" :render-sprite]
|
||||
|
||||
["/dashboard/team/:team-id"
|
||||
["/members" :dashboard-team-members]
|
||||
|
@ -172,6 +173,14 @@
|
|||
:page-id page-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
|
||||
(let [project-id (some-> params :path :project-id uuid)
|
||||
file-id (some-> params :path :file-id uuid)
|
||||
|
|
|
@ -123,3 +123,30 @@
|
|||
[:& object-svg {:objects @objects
|
||||
:object-id object-id
|
||||
: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)]]))])])))
|
||||
|
||||
|
|
|
@ -91,20 +91,22 @@
|
|||
[(str "penpot:" (d/name k)) v])]
|
||||
(into {} (map prefix-entry) m)))
|
||||
|
||||
|
||||
(mf/defc export-grid-data
|
||||
[{:keys [grids]}]
|
||||
[:> "penpot:grids" #js {}
|
||||
(for [{:keys [type display params]} grids]
|
||||
(let [props (->> (d/without-keys params [:color])
|
||||
(prefix-keys)
|
||||
(clj->js))]
|
||||
[:> "penpot:grid"
|
||||
(-> props
|
||||
(obj/set! "penpot:color" (get-in params [:color :color]))
|
||||
(obj/set! "penpot:opacity" (get-in params [:color :opacity]))
|
||||
(obj/set! "penpot:type" (d/name type))
|
||||
(cond-> (some? display)
|
||||
(obj/set! "penpot:display" (str display))))]))])
|
||||
(when-not (empty? grids)
|
||||
[:> "penpot:grids" #js {}
|
||||
(for [{:keys [type display params]} grids]
|
||||
(let [props (->> (d/without-keys params [:color])
|
||||
(prefix-keys)
|
||||
(clj->js))]
|
||||
[:> "penpot:grid"
|
||||
(-> props
|
||||
(obj/set! "penpot:color" (get-in params [:color :color]))
|
||||
(obj/set! "penpot:opacity" (get-in params [:color :opacity]))
|
||||
(obj/set! "penpot:type" (d/name type))
|
||||
(cond-> (some? display)
|
||||
(obj/set! "penpot:display" (str display))))]))]))
|
||||
|
||||
(mf/defc export-page
|
||||
[{:keys [options]}]
|
||||
|
@ -117,62 +119,84 @@
|
|||
[:> "penpot:page" #js {}
|
||||
[:& export-grid-data {:grids grids}]]))))
|
||||
|
||||
(mf/defc export-shadow-data
|
||||
[{:keys [shadow]}]
|
||||
(for [{:keys [style hidden color offset-x offset-y blur spread]} shadow]
|
||||
[:> "penpot:shadow"
|
||||
#js {:penpot:shadow-type (d/name style)
|
||||
:penpot:hidden (str hidden)
|
||||
:penpot:color (str (:color color))
|
||||
:penpot:opacity (str (:opacity color))
|
||||
:penpot:offset-x (str offset-x)
|
||||
:penpot:offset-y (str offset-y)
|
||||
:penpot:blur (str blur)
|
||||
:penpot:spread (str spread)}]))
|
||||
|
||||
(mf/defc export-blur-data [{:keys [blur]}]
|
||||
(when (some? blur)
|
||||
(let [{:keys [type hidden value]} blur]
|
||||
[:> "penpot:blur"
|
||||
#js {:penpot:blur-type (d/name type)
|
||||
:penpot:hidden (str hidden)
|
||||
:penpot:value (str value)}])))
|
||||
|
||||
(mf/defc export-exports-data [{:keys [exports]}]
|
||||
(for [{:keys [scale suffix type]} exports]
|
||||
[:> "penpot:export"
|
||||
#js {:penpot:type (d/name type)
|
||||
:penpot:suffix suffix
|
||||
:penpot:scale (str scale)}]))
|
||||
|
||||
(mf/defc export-svg-data [shape]
|
||||
[:*
|
||||
(when (contains? shape :svg-attrs)
|
||||
(let [svg-transform (get shape :svg-transform)
|
||||
svg-attrs (->> shape :svg-attrs keys (mapv d/name) (str/join ",") )
|
||||
svg-defs (->> shape :svg-defs keys (mapv d/name) (str/join ","))]
|
||||
[:> "penpot:svg-import"
|
||||
#js {:penpot:svg-attrs (when-not (empty? svg-attrs) svg-attrs)
|
||||
:penpot:svg-defs (when-not (empty? svg-defs) svg-defs)
|
||||
:penpot:svg-transform (when svg-transform (str svg-transform))
|
||||
:penpot:svg-viewbox-x (get-in shape [:svg-viewbox :x])
|
||||
:penpot:svg-viewbox-y (get-in shape [:svg-viewbox :y])
|
||||
:penpot:svg-viewbox-width (get-in shape [:svg-viewbox :width])
|
||||
:penpot:svg-viewbox-height (get-in shape [:svg-viewbox :height])}
|
||||
(for [[def-id def-xml] (:svg-defs shape)]
|
||||
[:> "penpot:svg-def" #js {:def-id def-id}
|
||||
[:& render-xml {:xml def-xml}]])]))
|
||||
|
||||
(when (= (:type shape) :svg-raw)
|
||||
(let [props
|
||||
(-> (obj/new)
|
||||
(obj/set! "penpot:x" (:x shape))
|
||||
(obj/set! "penpot:y" (:y shape))
|
||||
(obj/set! "penpot:width" (:width shape))
|
||||
(obj/set! "penpot:height" (:height shape))
|
||||
(obj/set! "penpot:tag" (-> (get-in shape [:content :tag]) d/name))
|
||||
(obj/merge! (-> (get-in shape [:content :attrs])
|
||||
(clj->js))))]
|
||||
[:> "penpot:svg-content" props
|
||||
(for [leaf (->> shape :content :content (filter string?))]
|
||||
[:> "penpot:svg-child" {} leaf])]))])
|
||||
|
||||
(mf/defc export-interactions-data
|
||||
[{:keys [interactions]}]
|
||||
(when-not (empty? interactions)
|
||||
[:> "penpot:interactions" #js {}
|
||||
(for [{:keys [action-type destination event-type]} interactions]
|
||||
[:> "penpot:interaction"
|
||||
#js {:penpot:action-type (d/name action-type)
|
||||
:penpot:destination (str destination)
|
||||
:penpot:event-type (d/name event-type)}])]))
|
||||
|
||||
(mf/defc export-data
|
||||
[{:keys [shape]}]
|
||||
(let [props (-> (obj/new)
|
||||
(add-data shape))]
|
||||
(let [props (-> (obj/new) (add-data shape))]
|
||||
[:> "penpot:shape" props
|
||||
(for [{:keys [style hidden color offset-x offset-y blur spread]} (:shadow shape)]
|
||||
[:> "penpot:shadow" #js {:penpot:shadow-type (d/name style)
|
||||
:penpot:hidden (str hidden)
|
||||
:penpot:color (str (:color color))
|
||||
:penpot:opacity (str (:opacity color))
|
||||
:penpot:offset-x (str offset-x)
|
||||
:penpot:offset-y (str offset-y)
|
||||
:penpot:blur (str blur)
|
||||
:penpot:spread (str spread)}])
|
||||
|
||||
(when (some? (:blur shape))
|
||||
(let [{:keys [type hidden value]} (:blur shape)]
|
||||
[:> "penpot:blur" #js {:penpot:blur-type (d/name type)
|
||||
:penpot:hidden (str hidden)
|
||||
:penpot:value (str value)}]))
|
||||
|
||||
(for [{:keys [scale suffix type]} (:exports shape)]
|
||||
[:> "penpot:export" #js {:penpot:type (d/name type)
|
||||
:penpot:suffix suffix
|
||||
:penpot:scale (str scale)}])
|
||||
|
||||
(when (contains? shape :svg-attrs)
|
||||
(let [svg-transform (get shape :svg-transform)
|
||||
svg-attrs (->> shape :svg-attrs keys (mapv d/name) (str/join ",") )
|
||||
svg-defs (->> shape :svg-defs keys (mapv d/name) (str/join ","))]
|
||||
[:> "penpot:svg-import" #js {:penpot:svg-attrs (when-not (empty? svg-attrs) svg-attrs)
|
||||
:penpot:svg-defs (when-not (empty? svg-defs) svg-defs)
|
||||
:penpot:svg-transform (when svg-transform (str svg-transform))
|
||||
:penpot:svg-viewbox-x (get-in shape [:svg-viewbox :x])
|
||||
:penpot:svg-viewbox-y (get-in shape [:svg-viewbox :y])
|
||||
:penpot:svg-viewbox-width (get-in shape [:svg-viewbox :width])
|
||||
:penpot:svg-viewbox-height (get-in shape [:svg-viewbox :height])}
|
||||
(for [[def-id def-xml] (:svg-defs shape)]
|
||||
[:> "penpot:svg-def" #js {:def-id def-id}
|
||||
[:& render-xml {:xml def-xml}]])]))
|
||||
|
||||
(when (= (:type shape) :svg-raw)
|
||||
(let [props (-> (obj/new)
|
||||
(obj/set! "penpot:x" (:x shape))
|
||||
(obj/set! "penpot:y" (:y shape))
|
||||
(obj/set! "penpot:width" (:width shape))
|
||||
(obj/set! "penpot:height" (:height shape))
|
||||
(obj/set! "penpot:tag" (-> (get-in shape [:content :tag]) d/name))
|
||||
(obj/merge! (-> (get-in shape [:content :attrs])
|
||||
(clj->js))))]
|
||||
[:> "penpot:svg-content" props
|
||||
(for [leaf (->> shape :content :content (filter string?))]
|
||||
[:> "penpot:svg-child" {} leaf])]))
|
||||
|
||||
|
||||
(when (and (= (:type shape) :frame)
|
||||
(seq (:grids shape)))
|
||||
[:& export-grid-data {:grids (:grids shape)}])]))
|
||||
[:& export-shadow-data shape]
|
||||
[:& export-blur-data shape]
|
||||
[:& export-exports-data shape]
|
||||
[:& export-svg-data shape]
|
||||
[:& export-interactions-data shape]
|
||||
[:& export-grid-data shape]]))
|
||||
|
||||
|
|
|
@ -286,9 +286,9 @@
|
|||
|
||||
shape-center (geom/center-shape shape)
|
||||
|
||||
on-resize (fn [current-position initial-position event]
|
||||
on-resize (fn [current-position _initial-position event]
|
||||
(dom/stop-propagation event)
|
||||
(st/emit! (dw/start-resize current-position initial-position selected shape)))
|
||||
(st/emit! (dw/start-resize current-position selected shape)))
|
||||
|
||||
on-rotate #(do (dom/stop-propagation %)
|
||||
(st/emit! (dw/start-rotate shapes)))]
|
||||
|
@ -311,9 +311,9 @@
|
|||
shape (geom/transform-shape shape {:round-coords? false})
|
||||
|
||||
shape' (if (debug? :simple-selection) (geom/setup {:type :rect} (geom/selection-rect [shape])) shape)
|
||||
on-resize (fn [current-position initial-position event]
|
||||
on-resize (fn [current-position _initial-position event]
|
||||
(dom/stop-propagation event)
|
||||
(st/emit! (dw/start-resize current-position initial-position #{shape-id} shape')))
|
||||
(st/emit! (dw/start-resize current-position #{shape-id} shape')))
|
||||
|
||||
on-rotate
|
||||
#(do (dom/stop-propagation %)
|
||||
|
|
|
@ -15,6 +15,12 @@
|
|||
[app.util.path.parser :as upp]
|
||||
[cuerdas.core :as str]))
|
||||
|
||||
(def url-regex
|
||||
#"url\(#([^\)]*)\)")
|
||||
|
||||
(def uuid-regex
|
||||
#"\w{8}-\w{4}-\w{4}-\w{4}-\w{12}")
|
||||
|
||||
(defn valid?
|
||||
[root]
|
||||
(contains? (:attrs root) :xmlns:penpot))
|
||||
|
@ -41,7 +47,7 @@
|
|||
(defn find-all-nodes
|
||||
[node tag]
|
||||
(when (some? node)
|
||||
(->> node :content (filterv #(= (:tag %) :defs)))))
|
||||
(->> node :content (filterv #(= (:tag %) tag)))))
|
||||
|
||||
(defn get-data
|
||||
([node]
|
||||
|
@ -65,6 +71,11 @@
|
|||
(or (close? node)
|
||||
(some? (get-data node))))
|
||||
|
||||
(defn get-id
|
||||
[node]
|
||||
(when-let [id (re-find uuid-regex (get-in node [:attrs :id]))]
|
||||
(uuid/uuid id)))
|
||||
|
||||
(defn str->bool
|
||||
[val]
|
||||
(when (some? val) (= val "true")))
|
||||
|
@ -115,7 +126,7 @@
|
|||
(defn without-penpot-prefix
|
||||
[m]
|
||||
(let [no-penpot-prefix?
|
||||
(fn [[k v]]
|
||||
(fn [[k _]]
|
||||
(not (str/starts-with? (d/name k) "penpot:")))]
|
||||
(into {} (filter no-penpot-prefix?) m)))
|
||||
|
||||
|
@ -180,11 +191,11 @@
|
|||
[props svg-data]
|
||||
(let [values (->> (select-keys svg-data [:cx :cy :rx :ry])
|
||||
(d/mapm (fn [_ val] (d/parse-double val))))]
|
||||
|
||||
{:x (- (:cx values) (:rx values))
|
||||
:y (- (:cy values) (:ry values))
|
||||
:width (* (:rx values) 2)
|
||||
:height (* (:ry values) 2)}))
|
||||
(-> props
|
||||
(assoc :x (- (:cx values) (:rx values))
|
||||
:y (- (:cy values) (:ry values))
|
||||
:width (* (:rx values) 2)
|
||||
:height (* (:ry values) 2)))))
|
||||
|
||||
(defn parse-path
|
||||
[props center svg-data]
|
||||
|
@ -193,9 +204,6 @@
|
|||
(assoc :content content)
|
||||
(assoc :center center))))
|
||||
|
||||
(def url-regex #"url\(#([^\)]*)\)")
|
||||
|
||||
|
||||
(defn parse-stops
|
||||
[gradient-node]
|
||||
(->> gradient-node
|
||||
|
@ -451,7 +459,7 @@
|
|||
[props node]
|
||||
(let [shadows (extract-from-data node :penpot:shadow parse-shadow)]
|
||||
(cond-> props
|
||||
(not (empty? shadows))
|
||||
(d/not-empty? shadows)
|
||||
(assoc :shadow shadows))))
|
||||
|
||||
(defn add-blur
|
||||
|
@ -465,7 +473,7 @@
|
|||
[props node]
|
||||
(let [exports (extract-from-data node :penpot:export parse-export)]
|
||||
(cond-> props
|
||||
(not (empty? exports))
|
||||
(d/not-empty? exports)
|
||||
(assoc :exports exports))))
|
||||
|
||||
(defn add-layer-options
|
||||
|
@ -483,7 +491,7 @@
|
|||
(defn remove-prefix [s]
|
||||
(cond-> s
|
||||
(string? s)
|
||||
(str/replace #"\w{8}-\w{4}-\w{4}-\w{4}-\w{12}-" "")))
|
||||
(str/replace (re-pattern (str uuid-regex "-")) "")))
|
||||
|
||||
(defn get-svg-attrs
|
||||
[svg-data svg-attrs]
|
||||
|
@ -499,7 +507,7 @@
|
|||
(reduce assoc-key {}))))
|
||||
|
||||
(defn get-svg-defs
|
||||
[node svg-defs]
|
||||
[node]
|
||||
|
||||
(let [svg-import (get-data node :penpot:svg-import)]
|
||||
(->> svg-import
|
||||
|
@ -537,7 +545,7 @@
|
|||
|
||||
|
||||
(some? svg-defs)
|
||||
(assoc :svg-defs (get-svg-defs node svg-defs))))
|
||||
(assoc :svg-defs (get-svg-defs node))))
|
||||
|
||||
props)))
|
||||
|
||||
|
@ -563,7 +571,7 @@
|
|||
(defn add-frame-data [props node]
|
||||
(let [grids (parse-grids node)]
|
||||
(cond-> props
|
||||
(not (empty? grids))
|
||||
(d/not-empty? grids)
|
||||
(assoc :grids grids))))
|
||||
|
||||
(defn has-image?
|
||||
|
@ -633,10 +641,19 @@
|
|||
background (:background style)
|
||||
grids (->> (parse-grids node)
|
||||
(group-by :type)
|
||||
(d/mapm (fn [k v] (-> v first :params))))]
|
||||
(d/mapm (fn [_ v] (-> v first :params))))]
|
||||
(cond-> {}
|
||||
(some? background)
|
||||
(assoc-in [:options :background] background)
|
||||
|
||||
(not (empty? grids))
|
||||
(d/not-empty? grids)
|
||||
(assoc-in [:options :saved-grids] grids))))
|
||||
|
||||
(defn parse-interactions
|
||||
[node]
|
||||
(let [interactions-node (get-data node :penpot:interactions)]
|
||||
(->> (find-all-nodes interactions-node :penpot:interaction)
|
||||
(mapv (fn [node]
|
||||
{:destination (get-meta node :destination uuid/uuid)
|
||||
:action-type (get-meta node :action-type keyword)
|
||||
:event-type (get-meta node :event-type keyword)})))))
|
|
@ -6,36 +6,57 @@
|
|||
|
||||
(ns app.worker.export
|
||||
(:require
|
||||
[app.common.data :as d]
|
||||
[app.config :as cfg]
|
||||
[app.main.render :as r]
|
||||
[app.main.repo :as rp]
|
||||
[app.util.dom :as dom]
|
||||
[app.util.zip :as uz]
|
||||
[app.util.http :as http]
|
||||
[app.util.json :as json]
|
||||
[app.util.zip :as uz]
|
||||
[app.worker.impl :as impl]
|
||||
[beicon.core :as rx]))
|
||||
[beicon.core :as rx]
|
||||
[cuerdas.core :as str]))
|
||||
|
||||
(defn create-manifest
|
||||
"Creates a manifest entry for the given files"
|
||||
[team-id files]
|
||||
[team-id file-id files]
|
||||
(letfn [(format-page [manifest page]
|
||||
(-> manifest
|
||||
(assoc (str (:id page))
|
||||
{:name (:name page)})))
|
||||
|
||||
(format-file [manifest file]
|
||||
(let [name (:name file)
|
||||
pages (->> (get-in file [:data :pages]) (mapv str))
|
||||
index (->> (get-in file [:data :pages-index]) (vals)
|
||||
(reduce format-page {}))]
|
||||
(let [name (:name file)
|
||||
is-shared (:is-shared file)
|
||||
pages (->> (get-in file [:data :pages])
|
||||
(mapv str))
|
||||
index (->> (get-in file [:data :pages-index])
|
||||
(vals)
|
||||
(reduce format-page {}))]
|
||||
(-> manifest
|
||||
(assoc (str (:id file))
|
||||
{:name name
|
||||
:pages pages
|
||||
:pagesIndex index}))))]
|
||||
{:name name
|
||||
:shared is-shared
|
||||
:pages pages
|
||||
:pagesIndex index
|
||||
:hasComponents (d/not-empty? (get-in file [:data :components]))
|
||||
:hasImages (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 {}))}]
|
||||
(json/encode manifest))))
|
||||
|
||||
(defn process-pages [file]
|
||||
(let [pages (get-in file [:data :pages])
|
||||
pages-index (get-in file [:data :pages-index])]
|
||||
(->> pages
|
||||
(map #(hash-map
|
||||
:file-id (:id file)
|
||||
:data (get pages-index %))))))
|
||||
|
||||
(defn get-page-data
|
||||
[{file-id :file-id {:keys [id name] :as data} :data}]
|
||||
(->> (r/render-page data)
|
||||
|
@ -45,30 +66,96 @@
|
|||
:file-id file-id
|
||||
:markup markup}))))
|
||||
|
||||
(defn process-pages [file]
|
||||
(let [pages (get-in file [:data :pages])
|
||||
pages-index (get-in file [:data :pages-index])]
|
||||
(->> pages
|
||||
(map #(hash-map
|
||||
:file-id (:id file)
|
||||
:data (get pages-index %))))))
|
||||
|
||||
(defn collect-page
|
||||
[{:keys [id file-id markup] :as page}]
|
||||
[(str file-id "/" id ".svg") markup])
|
||||
|
||||
(defn collect-entries [result data keys]
|
||||
(-> result
|
||||
(assoc (str (:id data))
|
||||
(->> (select-keys data keys)
|
||||
(d/deep-mapm
|
||||
(fn [[k v]]
|
||||
[(-> k str/camel) v]))))))
|
||||
|
||||
(def ^:const color-keys
|
||||
[:name :color :opacity :gradient])
|
||||
|
||||
(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
|
||||
[result typography]
|
||||
(collect-entries result typography typography-keys))
|
||||
|
||||
(defn collect-media
|
||||
[result media]
|
||||
(collect-entries result media media-keys))
|
||||
|
||||
(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
|
||||
[team-id file-id]
|
||||
|
||||
(let [files-stream
|
||||
(->> (rp/query :file {:id file-id})
|
||||
(->> (rx/merge (rp/query :file {:id file-id})
|
||||
(->> (rp/query :file-libraries {:file-id file-id})
|
||||
(rx/flat-map identity)
|
||||
(rx/map #(assoc % :is-shared true))))
|
||||
(rx/reduce #(assoc %1 (:id %2) %2) {})
|
||||
(rx/share))
|
||||
|
||||
manifest-stream
|
||||
(->> files-stream
|
||||
(rx/map #(create-manifest team-id %))
|
||||
(rx/map #(create-manifest team-id file-id %))
|
||||
(rx/map #(vector "manifest.json" %)))
|
||||
|
||||
render-stream
|
||||
|
@ -79,6 +166,33 @@
|
|||
(rx/flat-map get-page-data)
|
||||
(rx/share))
|
||||
|
||||
colors-stream
|
||||
(->> files-stream
|
||||
(rx/flat-map vals)
|
||||
(rx/map #(vector (:id %) (get-in % [:data :colors])))
|
||||
(rx/filter #(d/not-empty? (second %)))
|
||||
(rx/map parse-library-color))
|
||||
|
||||
typographies-stream
|
||||
(->> files-stream
|
||||
(rx/flat-map vals)
|
||||
(rx/map #(vector (:id %) (get-in % [:data :typographies])))
|
||||
(rx/filter #(d/not-empty? (second %)))
|
||||
(rx/map parse-library-typographies))
|
||||
|
||||
media-stream
|
||||
(->> files-stream
|
||||
(rx/flat-map vals)
|
||||
(rx/map #(vector (:id %) (get-in % [:data :media])))
|
||||
(rx/filter #(d/not-empty? (second %)))
|
||||
(rx/flat-map parse-library-media))
|
||||
|
||||
components-stream
|
||||
(->> files-stream
|
||||
(rx/flat-map vals)
|
||||
(rx/filter #(d/not-empty? (get-in % [:data :components])))
|
||||
(rx/flat-map parse-library-components))
|
||||
|
||||
pages-stream
|
||||
(->> render-stream
|
||||
(rx/map collect-page))]
|
||||
|
@ -90,7 +204,13 @@
|
|||
:file file-id
|
||||
:data (str "Render " (:file-name %) " - " (:name %)))))
|
||||
|
||||
(->> (rx/merge manifest-stream pages-stream)
|
||||
(->> (rx/merge
|
||||
manifest-stream
|
||||
pages-stream
|
||||
components-stream
|
||||
media-stream
|
||||
colors-stream
|
||||
typographies-stream)
|
||||
(rx/reduce conj [])
|
||||
(rx/with-latest-from files-stream)
|
||||
(rx/flat-map (fn [[data files]]
|
||||
|
@ -98,7 +218,7 @@
|
|||
(rx/map #(vector (get files file-id) %)))))))))
|
||||
|
||||
(defmethod impl/handler :export-file
|
||||
[{:keys [team-id project-id files] :as message}]
|
||||
[{:keys [team-id files] :as message}]
|
||||
|
||||
(->> (rx/from files)
|
||||
(rx/mapcat #(export-file team-id %))
|
||||
|
|
|
@ -17,7 +17,6 @@
|
|||
[app.util.zip :as uz]
|
||||
[app.worker.impl :as impl]
|
||||
[beicon.core :as rx]
|
||||
[cuerdas.core :as str]
|
||||
[tubax.core :as tubax]))
|
||||
|
||||
;; Upload changes batches size
|
||||
|
@ -25,12 +24,13 @@
|
|||
|
||||
(defn create-file
|
||||
"Create a new file on the back-end"
|
||||
[project-id name]
|
||||
[project-id file-desc]
|
||||
(let [file-id (uuid/next)]
|
||||
(rp/mutation
|
||||
:create-temp-file
|
||||
{:id file-id
|
||||
:name name
|
||||
:name (:name file-desc)
|
||||
:is-shared (:shared file-desc)
|
||||
:project-id project-id
|
||||
:data (-> cp/empty-file-data (assoc :id file-id))})))
|
||||
|
||||
|
@ -78,36 +78,65 @@
|
|||
(defn add-shape-file
|
||||
[file node]
|
||||
|
||||
(let [type (cip/get-type node)
|
||||
close? (cip/close? node)
|
||||
data (cip/parse-data type node)]
|
||||
|
||||
(let [type (cip/get-type node)
|
||||
close? (cip/close? node)]
|
||||
(if close?
|
||||
(case type
|
||||
:frame
|
||||
(fb/close-artboard file)
|
||||
:frame (fb/close-artboard file)
|
||||
:group (fb/close-group file)
|
||||
:svg-raw (fb/close-svg-raw file)
|
||||
#_default file)
|
||||
|
||||
:group
|
||||
(fb/close-group file)
|
||||
(let [data (cip/parse-data type node)
|
||||
old-id (cip/get-id node)
|
||||
interactions (cip/parse-interactions node)
|
||||
|
||||
:svg-raw
|
||||
(fb/close-svg-raw file)
|
||||
file (case type
|
||||
:frame (fb/add-artboard file data)
|
||||
:group (fb/add-group file data)
|
||||
:rect (fb/create-rect file data)
|
||||
:circle (fb/create-circle file data)
|
||||
:path (fb/create-path file data)
|
||||
:text (fb/create-text file data)
|
||||
:image (fb/create-image file data)
|
||||
:svg-raw (fb/create-svg-raw file data)
|
||||
#_default file)]
|
||||
|
||||
;; default
|
||||
file)
|
||||
(assert (some? old-id) "ID not found")
|
||||
|
||||
(case type
|
||||
:frame (fb/add-artboard file data)
|
||||
:group (fb/add-group file data)
|
||||
:rect (fb/create-rect file data)
|
||||
:circle (fb/create-circle file data)
|
||||
:path (fb/create-path file data)
|
||||
:text (fb/create-text file data)
|
||||
:image (fb/create-image file data)
|
||||
:svg-raw (fb/create-svg-raw file data)
|
||||
;; We store this data for post-processing after every shape has been
|
||||
;; added
|
||||
(cond-> file
|
||||
(some? (:last-id file))
|
||||
(assoc-in [:id-mapping old-id] (:last-id file))
|
||||
|
||||
;; default
|
||||
file))))
|
||||
(d/not-empty? interactions)
|
||||
(assoc-in [:interactions old-id] interactions))))))
|
||||
|
||||
(defn post-process-file
|
||||
[file]
|
||||
|
||||
(letfn [(add-interaction
|
||||
[id file {:keys [action-type event-type destination] :as interaction}]
|
||||
(fb/add-interaction file action-type event-type id destination))
|
||||
|
||||
(add-interactions
|
||||
[file [old-id interactions]]
|
||||
(let [id (get-in file [:id-mapping old-id])]
|
||||
(->> interactions
|
||||
(mapv (fn [interaction]
|
||||
(let [id (get-in file [:id-mapping (:destination interaction)])]
|
||||
(assoc interaction :destination id))))
|
||||
(reduce
|
||||
(partial add-interaction id) file))))
|
||||
|
||||
(process-interactions
|
||||
[file]
|
||||
(reduce add-interactions file (:interactions file)))]
|
||||
|
||||
(-> file
|
||||
(process-interactions)
|
||||
(dissoc :id-mapping :interactions))))
|
||||
|
||||
(defn merge-reduce [f seed ob]
|
||||
(->> (rx/concat
|
||||
|
@ -145,6 +174,7 @@
|
|||
(rx/filter cip/shape?)
|
||||
(rx/mapcat (partial resolve-images file-id))
|
||||
(rx/reduce add-shape-file (fb/add-page file page-data))
|
||||
(rx/map post-process-file)
|
||||
(rx/map fb/close-page)))
|
||||
(rx/empty)))
|
||||
|
||||
|
@ -173,7 +203,7 @@
|
|||
(rx/flat-map (comp :files json/decode :content))
|
||||
(rx/flat-map
|
||||
(fn [[file-id file-desc]]
|
||||
(->> (create-file project-id (:name file-desc))
|
||||
(->> (create-file project-id file-desc)
|
||||
(rx/flat-map #(process-file % file-id file-desc zip-file)))))))
|
||||
|
||||
(defmethod impl/handler :import-file
|
||||
|
|
Loading…
Add table
Reference in a new issue