mirror of
https://github.com/penpot/penpot.git
synced 2025-03-12 15:51:37 -05:00
✨ Process interactions on import
This commit is contained in:
parent
4e909dc369
commit
4d0dcc5876
4 changed files with 192 additions and 95 deletions
|
@ -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))
|
||||
|
|
|
@ -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,85 @@
|
|||
[:> "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))
|
||||
frame? (= (:type shape) :frame)]
|
||||
[:> "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]]))
|
||||
|
||||
|
|
|
@ -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")))
|
||||
|
@ -193,9 +204,6 @@
|
|||
(assoc :content content)
|
||||
(assoc :center center))))
|
||||
|
||||
(def url-regex #"url\(#([^\)]*)\)")
|
||||
|
||||
|
||||
(defn parse-stops
|
||||
[gradient-node]
|
||||
(->> gradient-node
|
||||
|
@ -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]
|
||||
|
@ -640,3 +648,12 @@
|
|||
|
||||
(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)})))))
|
||||
|
|
|
@ -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))))
|
||||
(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)))
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue