0
Fork 0
mirror of https://github.com/penpot/penpot.git synced 2025-02-10 00:58:26 -05:00

Add support to export/import guides

This commit is contained in:
alonso.torres 2022-01-21 13:52:21 +01:00
parent d356a3fa56
commit f303d7b33e
10 changed files with 165 additions and 109 deletions

View file

@ -75,7 +75,7 @@
:opt-un [:guides/frame-id]))
(s/def ::guides
(s/map-of uuid? ::shape))
(s/map-of uuid? ::guide))
;; --- Options

View file

@ -84,7 +84,8 @@
:snap-grid
:scale-text
:dynamic-alignment
:display-artboard-names})
:display-artboard-names
:snap-guides})
(s/def ::layout-flags (s/coll-of ::layout-flag))
@ -96,7 +97,8 @@
:display-grid
:snap-grid
:dynamic-alignment
:display-artboard-names})
:display-artboard-names
:snap-guides})
(def layout-presets
{:assets

View file

@ -204,7 +204,9 @@
:height "100%"
:background background-color}}
[:& export/export-page {:options (:options data)}]
(when include-metadata?
[:& export/export-page {:options (:options data)}])
[:& ff/fontfaces-style {:shapes root-children}]
(for [item shapes]
(let [frame? (= (:type item) :frame)]

View file

@ -24,12 +24,30 @@
(def ^:const snap-distance-accuracy 10)
(defn- remove-from-snap-points
[remove-id?]
[remove-snap?]
(fn [query-result]
(->> query-result
(map (fn [[value data]] [value (remove (comp remove-id? :id) data)]))
(map (fn [[value data]] [value (remove remove-snap? data)]))
(filter (fn [[_ data]] (seq data))))))
(defn make-remove-snap
"Creates a filter for the snap data. Used to disable certain layouts"
[layout filter-shapes]
(fn [{:keys [type id]}]
(cond
(= type :layout)
(or (not (contains? layout :display-grid))
(not (contains? layout :snap-grid)))
(= type :guide)
(or (not (contains? layout :rules))
(not (contains? layout :snap-guides)))
:else
(or (contains? filter-shapes id)
(not (contains? layout :dynamic-alignment))))))
(defn- flatten-to-points
[query-result]
(mapcat (fn [[_ data]] (map :pt data)) query-result))
@ -57,7 +75,7 @@
;; Otherwise the root frame is the common
:else zero)))
(defn get-snap-points [page-id frame-id filter-shapes point coord]
(defn get-snap-points [page-id frame-id remove-snap? point coord]
(let [value (get point coord)]
(->> (uw/ask! {:cmd :snaps/range-query
:page-id page-id
@ -65,11 +83,11 @@
:axis coord
:ranges [[(- value 0.5) (+ value 0.5)]]})
(rx/first)
(rx/map (remove-from-snap-points filter-shapes))
(rx/map (remove-from-snap-points remove-snap?))
(rx/map flatten-to-points))))
(defn- search-snap
[page-id frame-id points coord filter-shapes zoom]
[page-id frame-id points coord remove-snap? zoom]
(let [snap-accuracy (/ snap-accuracy zoom)
ranges (->> points
(map coord)
@ -81,7 +99,7 @@
:axis coord
:ranges ranges})
(rx/first)
(rx/map (remove-from-snap-points filter-shapes))
(rx/map (remove-from-snap-points remove-snap?))
(rx/map (get-min-distance-snap points coord)))))
(defn snap->vector [[[from-x to-x] [from-y to-y]]]
@ -91,13 +109,12 @@
(gpt/to-vec from to))))
(defn- closest-snap
[page-id frame-id points filter-shapes zoom]
(let [snap-x (search-snap page-id frame-id points :x filter-shapes zoom)
snap-y (search-snap page-id frame-id points :y filter-shapes zoom)]
[page-id frame-id points remove-snap? zoom]
(let [snap-x (search-snap page-id frame-id points :x remove-snap? zoom)
snap-y (search-snap page-id frame-id points :y remove-snap? zoom)]
(->> (rx/combine-latest snap-x snap-y)
(rx/map snap->vector))))
(defn sr-distance [coord sr1 sr2]
(let [c1 (if (= coord :x) :x1 :y1)
c2 (if (= coord :x) :x2 :y2)
@ -209,12 +226,8 @@
[page-id shapes layout zoom point]
(let [frame-id (snap-frame-id shapes)
filter-shapes (into #{} (map :id shapes))
filter-shapes (fn [id] (if (= id :layout)
(or (not (contains? layout :display-grid))
(not (contains? layout :snap-grid)))
(or (filter-shapes id)
(not (contains? layout :dynamic-alignment)))))]
(->> (closest-snap page-id frame-id [point] filter-shapes zoom)
remove-snap? (make-remove-snap layout filter-shapes)]
(->> (closest-snap page-id frame-id [point] remove-snap? zoom)
(rx/map #(or % (gpt/point 0 0)))
(rx/map #(gpt/add point %)))))
@ -222,11 +235,8 @@
[page-id shapes objects layout zoom movev]
(let [frame-id (snap-frame-id shapes)
filter-shapes (into #{} (map :id shapes))
filter-shapes (fn [id] (if (= id :layout)
(or (not (contains? layout :display-grid))
(not (contains? layout :snap-grid)))
(or (filter-shapes id)
(not (contains? layout :dynamic-alignment)))))
remove-snap? (make-remove-snap layout filter-shapes)
shape (if (> (count shapes) 1)
(->> shapes (map gsh/transform-shape) gsh/selection-rect (gsh/setup {:type :rect}))
(->> shapes (first)))
@ -236,7 +246,7 @@
;; Move the points in the translation vector
(map #(gpt/add % movev)))]
(->> (rx/merge (closest-snap page-id frame-id shapes-points filter-shapes zoom)
(->> (rx/merge (closest-snap page-id frame-id shapes-points remove-snap? zoom)
(when (contains? layout :dynamic-alignment)
(closest-distance-snap page-id shapes objects zoom movev)))
(rx/reduce gpt/min)

View file

@ -155,20 +155,30 @@
:name name
:starting-frame starting-frame}])])
(mf/defc export-guides
[{:keys [guides]}]
[:> "penpot:guides" #js {}
(for [{:keys [id position frame-id axis]} (vals guides)]
[:> "penpot:guide" #js {:position position
:frame-id frame-id
:axis (d/name axis)}])])
(mf/defc export-page
[{:keys [options]}]
(let [saved-grids (get options :saved-grids)
flows (get options :flows)]
(when (or (seq saved-grids) (seq flows))
(let [parse-grid
(fn [[type params]]
{:type type :params params})
flows (get options :flows)
guides (get options :guides)]
[:> "penpot:page" #js {}
(when (d/not-empty? saved-grids)
(let [parse-grid (fn [[type params]] {:type type :params params})
grids (->> saved-grids (mapv parse-grid))]
[:> "penpot:page" #js {}
(when (seq saved-grids)
[:& export-grid-data {:grids grids}])
(when (seq flows)
[:& export-flows {:flows flows}])]))))
[:& export-grid-data {:grids grids}]))
(when (d/not-empty? flows)
[:& export-flows {:flows flows}])
(when (d/not-empty? guides)
[:& export-guides {:guides guides}])]))
(defn- export-shadow-data [{:keys [shadow]}]
(mf/html

View file

@ -158,7 +158,7 @@
show-rules? (contains? layout :rules)
;; TODO
show-guides? true]
disabled-guides? (or drawing-tool transform)]
(hooks/setup-dom-events viewport-ref zoom disable-paste in-viewport?)
(hooks/setup-viewport-size viewport-ref)
@ -361,13 +361,14 @@
[:& widgets/viewport-actions]
(when show-rules?
[:& rules/rules
{:zoom zoom
:vbox vbox}])
[:*
[:& rules/rules
{:zoom zoom
:vbox vbox}]
(when show-guides?
[:& guides/viewport-guides
{:zoom zoom
:vbox vbox
:hover-frame frame-parent}])]]]))
[:& guides/viewport-guides
{:zoom zoom
:vbox vbox
:hover-frame frame-parent
:disabled-guides? disabled-guides?}]])]]]))

View file

@ -234,7 +234,7 @@
(mf/defc guide
{::mf/wrap [mf/memo]}
[{:keys [guide hover? on-guide-change get-hover-frame vbox zoom hover-frame]}]
[{:keys [guide hover? on-guide-change get-hover-frame vbox zoom hover-frame disabled-guides?]}]
(let [axis (:axis guide)
@ -260,20 +260,21 @@
guide-pill-corner-radius (/ guide-pill-corner-radius zoom)]
[:g.guide-area
(let [{:keys [x y width height]} (guide-area-axis pos vbox zoom frame axis)]
[:rect {:x x
:y y
:width width
:height height
:style {:fill "none"
:pointer-events "fill"
:cursor (if (= axis :x) "ew-resize" "ns-resize")}
:on-pointer-enter on-pointer-enter
:on-pointer-leave on-pointer-leave
:on-pointer-down on-pointer-down
:on-pointer-up on-pointer-up
:on-lost-pointer-capture on-lost-pointer-capture
:on-mouse-move on-mouse-move}])
(when-not disabled-guides?
(let [{:keys [x y width height]} (guide-area-axis pos vbox zoom frame axis)]
[:rect {:x x
:y y
:width width
:height height
:style {:fill "none"
:pointer-events "fill"
:cursor (if (= axis :x) "ew-resize" "ns-resize")}
:on-pointer-enter on-pointer-enter
:on-pointer-leave on-pointer-leave
:on-pointer-down on-pointer-down
:on-pointer-up on-pointer-up
:on-lost-pointer-capture on-lost-pointer-capture
:on-mouse-move on-mouse-move}]))
(if (some? frame)
(let [{:keys [l1-x1 l1-y1 l1-x2 l1-y2
@ -345,7 +346,7 @@
(str (mth/round pos))]]))]))
(mf/defc new-guide-area
[{:keys [vbox zoom axis get-hover-frame]}]
[{:keys [vbox zoom axis get-hover-frame disabled-guides?]}]
(let [on-guide-change
(mf/use-callback
@ -367,20 +368,21 @@
frame]} (use-guide on-guide-change get-hover-frame zoom {:axis axis})]
[:g.new-guides
(let [{:keys [x y width height]} (guide-creation-area vbox zoom axis)]
[:rect {:x x
:y y
:width width
:height height
:on-pointer-enter on-pointer-enter
:on-pointer-leave on-pointer-leave
:on-pointer-down on-pointer-down
:on-pointer-up on-pointer-up
:on-lost-pointer-capture on-lost-pointer-capture
:on-mouse-move on-mouse-move
:style {:fill "none"
:pointer-events "fill"
:cursor (if (= axis :x) "ew-resize" "ns-resize")}}])
(when-not disabled-guides?
(let [{:keys [x y width height]} (guide-creation-area vbox zoom axis)]
[:rect {:x x
:y y
:width width
:height height
:on-pointer-enter on-pointer-enter
:on-pointer-leave on-pointer-leave
:on-pointer-down on-pointer-down
:on-pointer-up on-pointer-up
:on-lost-pointer-capture on-lost-pointer-capture
:on-mouse-move on-mouse-move
:style {:fill "none"
:pointer-events "fill"
:cursor (if (= axis :x) "ew-resize" "ns-resize")}}]))
(when (:new-position @state)
[:& guide {:guide {:axis axis
@ -393,12 +395,15 @@
(mf/defc viewport-guides
{::mf/wrap [mf/memo]}
[{:keys [zoom vbox hover-frame]}]
[{:keys [zoom vbox hover-frame disabled-guides?]}]
(let [page (mf/deref refs/workspace-page)
guides (->> (get-in page [:options :guides] {})
(vals)
(filter (guide-inside-vbox? vbox)))
guides (mf/use-memo
(mf/deps page vbox)
#(->> (get-in page [:options :guides] {})
(vals)
(filter (guide-inside-vbox? vbox))))
hover-frame-ref (mf/use-ref nil)
@ -417,16 +422,23 @@
(st/emit! (dw/update-guides guide))
(st/emit! (dw/remove-guide guide)))))]
#_(mf/use-effect (mf/deps guides) #(.log js/console (clj->js guides)))
(mf/use-effect
(mf/deps hover-frame)
(fn []
#_(.log js/console "set" (clj->js hover-frame))
(mf/set-ref-val! hover-frame-ref hover-frame)))
[:g.guides {:pointer-events "none"}
[:& new-guide-area {:vbox vbox :zoom zoom :axis :x :get-hover-frame get-hover-frame}]
[:& new-guide-area {:vbox vbox :zoom zoom :axis :y :get-hover-frame get-hover-frame}]
[:& new-guide-area {:vbox vbox
:zoom zoom
:axis :x
:get-hover-frame get-hover-frame
:disabled-guides? disabled-guides?}]
[:& new-guide-area {:vbox vbox
:zoom zoom
:axis :y
:get-hover-frame get-hover-frame
:disabled-guides? disabled-guides?}]
(for [current guides]
[:& guide {:key (str "guide-" (:id current))
@ -434,5 +446,6 @@
:vbox vbox
:zoom zoom
:get-hover-frame get-hover-frame
:on-guide-change on-guide-change}])]))
:on-guide-change on-guide-change
:disabled-guides? disabled-guides?}])]))

View file

@ -52,7 +52,7 @@
:opacity line-opacity}])
(defn get-snap
[coord {:keys [shapes page-id filter-shapes modifiers]}]
[coord {:keys [shapes page-id remove-snap? modifiers]}]
(let [shape (if (> (count shapes) 1)
(->> shapes (map gsh/transform-shape) gsh/selection-rect (gsh/setup {:type :rect}))
(->> shapes (first)))
@ -68,7 +68,7 @@
(->> (sp/shape-snap-points shape)
(map #(vector frame-id %)))))
(rx/flat-map (fn [[frame-id point]]
(->> (snap/get-snap-points page-id frame-id filter-shapes point coord)
(->> (snap/get-snap-points page-id frame-id remove-snap? point coord)
(rx/map #(vector point % coord)))))
(rx/reduce conj []))))
@ -104,7 +104,7 @@
(hash-map coord fixedv (flip coord) maxv)]))))
(mf/defc snap-feedback
[{:keys [shapes filter-shapes zoom modifiers] :as props}]
[{:keys [shapes remove-snap? zoom modifiers] :as props}]
(let [state (mf/use-state [])
subject (mf/use-memo #(rx/subject))
@ -129,7 +129,7 @@
#(rx/dispose! sub))))
(mf/use-effect
(mf/deps shapes filter-shapes modifiers)
(mf/deps shapes remove-snap? modifiers)
(fn []
(rx/push! subject props)))
@ -152,29 +152,26 @@
{::mf/wrap [mf/memo]}
[{:keys [layout zoom objects selected page-id drawing transform modifiers] :as props}]
(let [;; shapes (mf/deref (refs/objects-by-id selected))
;; filter-shapes (mf/deref refs/selected-shapes-with-children)
(let [shapes
(->> selected
(map #(get objects %))
(filterv (comp not nil?)))
shapes (->> selected
(map #(get objects %))
(filterv (comp not nil?)))
filter-shapes (into #{}
(comp (mapcat #(cp/get-object-with-children % objects))
(map :id))
selected)
filter-shapes
(into #{}
(comp (mapcat #(cp/get-object-with-children % objects))
(map :id))
selected)
filter-shapes (fn [id]
(if (= id :layout)
(or (not (contains? layout :display-grid))
(not (contains? layout :snap-grid)))
(or (filter-shapes id)
(not (contains? layout :dynamic-alignment)))))
remove-snap? (mf/use-memo
(mf/deps layout filter-shapes)
#(snap/make-remove-snap layout filter-shapes))
shapes (if drawing [drawing] shapes)]
(when (or drawing transform)
[:& snap-feedback {:shapes shapes
:page-id page-id
:filter-shapes filter-shapes
:remove-snap? remove-snap?
:zoom zoom
:modifiers modifiers}])))

View file

@ -515,6 +515,20 @@
(let [flows-node (get-data node :penpot:flows)]
(->> flows-node :content (mapv parse-flow-node))))
(defn parse-guide-node [node]
(let [attrs (-> node :attrs remove-penpot-prefix)]
(println attrs)
(let [id (uuid/next)]
[id
{:id id
:frame-id (when (:frame-id attrs) (-> attrs :frame-id uuid))
:axis (-> attrs :axis keyword)
:position (-> attrs :position d/parse-double)}])))
(defn parse-guides [node]
(let [guides-node (get-data node :penpot:guides)]
(->> guides-node :content (map parse-guide-node) (into {}))))
(defn extract-from-data
([node tag]
(extract-from-data node tag identity))
@ -764,7 +778,8 @@
grids (->> (parse-grids node)
(group-by :type)
(d/mapm (fn [_ v] (-> v first :params))))
flows (parse-flows node)]
flows (parse-flows node)
guides (parse-guides node)]
(cond-> {}
(some? background)
(assoc-in [:options :background] background)
@ -773,7 +788,10 @@
(assoc-in [:options :saved-grids] grids)
(d/not-empty? flows)
(assoc-in [:options :flows] flows))))
(assoc-in [:options :flows] flows)
(d/not-empty? guides)
(assoc-in [:options :guides] guides))))
(defn parse-interactions
[node]

View file

@ -283,7 +283,6 @@
(defn setup-interactions
[file]
(letfn [(add-interactions
[file [id interactions]]
(->> interactions
@ -294,7 +293,6 @@
(let [interactions (:interactions file)
file (dissoc file :interactions)]
(->> interactions (reduce add-interactions file))))]
(-> file process-interactions)))
(defn resolve-media
@ -328,7 +326,12 @@
(assoc :id (resolve page-id)))
flows (->> (get-in page-data [:options :flows])
(mapv #(update % :starting-frame resolve)))
page-data (d/assoc-in-when page-data [:options :flows] flows)
guides (->> (get-in page-data [:options :guides])
(d/mapm #(update %2 :frame-id resolve)))
page-data (-> page-data
(d/assoc-in-when [:options :flows] flows)
(d/assoc-in-when [:options :guides] guides))
file (-> file (fb/add-page page-data))]
(->> (rx/from nodes)
(rx/filter cip/shape?)