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])) :opt-un [:guides/frame-id]))
(s/def ::guides (s/def ::guides
(s/map-of uuid? ::shape)) (s/map-of uuid? ::guide))
;; --- Options ;; --- Options

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -234,7 +234,7 @@
(mf/defc guide (mf/defc guide
{::mf/wrap [mf/memo]} {::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) (let [axis (:axis guide)
@ -260,20 +260,21 @@
guide-pill-corner-radius (/ guide-pill-corner-radius zoom)] guide-pill-corner-radius (/ guide-pill-corner-radius zoom)]
[:g.guide-area [:g.guide-area
(let [{:keys [x y width height]} (guide-area-axis pos vbox zoom frame axis)] (when-not disabled-guides?
[:rect {:x x (let [{:keys [x y width height]} (guide-area-axis pos vbox zoom frame axis)]
:y y [:rect {:x x
:width width :y y
:height height :width width
:style {:fill "none" :height height
:pointer-events "fill" :style {:fill "none"
:cursor (if (= axis :x) "ew-resize" "ns-resize")} :pointer-events "fill"
:on-pointer-enter on-pointer-enter :cursor (if (= axis :x) "ew-resize" "ns-resize")}
:on-pointer-leave on-pointer-leave :on-pointer-enter on-pointer-enter
:on-pointer-down on-pointer-down :on-pointer-leave on-pointer-leave
:on-pointer-up on-pointer-up :on-pointer-down on-pointer-down
:on-lost-pointer-capture on-lost-pointer-capture :on-pointer-up on-pointer-up
:on-mouse-move on-mouse-move}]) :on-lost-pointer-capture on-lost-pointer-capture
:on-mouse-move on-mouse-move}]))
(if (some? frame) (if (some? frame)
(let [{:keys [l1-x1 l1-y1 l1-x2 l1-y2 (let [{:keys [l1-x1 l1-y1 l1-x2 l1-y2
@ -345,7 +346,7 @@
(str (mth/round pos))]]))])) (str (mth/round pos))]]))]))
(mf/defc new-guide-area (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 (let [on-guide-change
(mf/use-callback (mf/use-callback
@ -367,20 +368,21 @@
frame]} (use-guide on-guide-change get-hover-frame zoom {:axis axis})] frame]} (use-guide on-guide-change get-hover-frame zoom {:axis axis})]
[:g.new-guides [:g.new-guides
(let [{:keys [x y width height]} (guide-creation-area vbox zoom axis)] (when-not disabled-guides?
[:rect {:x x (let [{:keys [x y width height]} (guide-creation-area vbox zoom axis)]
:y y [:rect {:x x
:width width :y y
:height height :width width
:on-pointer-enter on-pointer-enter :height height
:on-pointer-leave on-pointer-leave :on-pointer-enter on-pointer-enter
:on-pointer-down on-pointer-down :on-pointer-leave on-pointer-leave
:on-pointer-up on-pointer-up :on-pointer-down on-pointer-down
:on-lost-pointer-capture on-lost-pointer-capture :on-pointer-up on-pointer-up
:on-mouse-move on-mouse-move :on-lost-pointer-capture on-lost-pointer-capture
:style {:fill "none" :on-mouse-move on-mouse-move
:pointer-events "fill" :style {:fill "none"
:cursor (if (= axis :x) "ew-resize" "ns-resize")}}]) :pointer-events "fill"
:cursor (if (= axis :x) "ew-resize" "ns-resize")}}]))
(when (:new-position @state) (when (:new-position @state)
[:& guide {:guide {:axis axis [:& guide {:guide {:axis axis
@ -393,12 +395,15 @@
(mf/defc viewport-guides (mf/defc viewport-guides
{::mf/wrap [mf/memo]} {::mf/wrap [mf/memo]}
[{:keys [zoom vbox hover-frame]}] [{:keys [zoom vbox hover-frame disabled-guides?]}]
(let [page (mf/deref refs/workspace-page) (let [page (mf/deref refs/workspace-page)
guides (->> (get-in page [:options :guides] {})
(vals) guides (mf/use-memo
(filter (guide-inside-vbox? vbox))) (mf/deps page vbox)
#(->> (get-in page [:options :guides] {})
(vals)
(filter (guide-inside-vbox? vbox))))
hover-frame-ref (mf/use-ref nil) hover-frame-ref (mf/use-ref nil)
@ -417,16 +422,23 @@
(st/emit! (dw/update-guides guide)) (st/emit! (dw/update-guides guide))
(st/emit! (dw/remove-guide guide)))))] (st/emit! (dw/remove-guide guide)))))]
#_(mf/use-effect (mf/deps guides) #(.log js/console (clj->js guides)))
(mf/use-effect (mf/use-effect
(mf/deps hover-frame) (mf/deps hover-frame)
(fn [] (fn []
#_(.log js/console "set" (clj->js hover-frame))
(mf/set-ref-val! hover-frame-ref hover-frame))) (mf/set-ref-val! hover-frame-ref hover-frame)))
[:g.guides {:pointer-events "none"} [: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
[:& new-guide-area {:vbox vbox :zoom zoom :axis :y :get-hover-frame get-hover-frame}] :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] (for [current guides]
[:& guide {:key (str "guide-" (:id current)) [:& guide {:key (str "guide-" (:id current))
@ -434,5 +446,6 @@
:vbox vbox :vbox vbox
:zoom zoom :zoom zoom
:get-hover-frame get-hover-frame :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}]) :opacity line-opacity}])
(defn get-snap (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) (let [shape (if (> (count shapes) 1)
(->> shapes (map gsh/transform-shape) gsh/selection-rect (gsh/setup {:type :rect})) (->> shapes (map gsh/transform-shape) gsh/selection-rect (gsh/setup {:type :rect}))
(->> shapes (first))) (->> shapes (first)))
@ -68,7 +68,7 @@
(->> (sp/shape-snap-points shape) (->> (sp/shape-snap-points shape)
(map #(vector frame-id %))))) (map #(vector frame-id %)))))
(rx/flat-map (fn [[frame-id point]] (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/map #(vector point % coord)))))
(rx/reduce conj [])))) (rx/reduce conj []))))
@ -104,7 +104,7 @@
(hash-map coord fixedv (flip coord) maxv)])))) (hash-map coord fixedv (flip coord) maxv)]))))
(mf/defc snap-feedback (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 []) (let [state (mf/use-state [])
subject (mf/use-memo #(rx/subject)) subject (mf/use-memo #(rx/subject))
@ -129,7 +129,7 @@
#(rx/dispose! sub)))) #(rx/dispose! sub))))
(mf/use-effect (mf/use-effect
(mf/deps shapes filter-shapes modifiers) (mf/deps shapes remove-snap? modifiers)
(fn [] (fn []
(rx/push! subject props))) (rx/push! subject props)))
@ -152,29 +152,26 @@
{::mf/wrap [mf/memo]} {::mf/wrap [mf/memo]}
[{:keys [layout zoom objects selected page-id drawing transform modifiers] :as props}] [{:keys [layout zoom objects selected page-id drawing transform modifiers] :as props}]
(let [;; shapes (mf/deref (refs/objects-by-id selected)) (let [shapes
;; filter-shapes (mf/deref refs/selected-shapes-with-children) (->> selected
(map #(get objects %))
(filterv (comp not nil?)))
shapes (->> selected filter-shapes
(map #(get objects %)) (into #{}
(filterv (comp not nil?))) (comp (mapcat #(cp/get-object-with-children % objects))
filter-shapes (into #{} (map :id))
(comp (mapcat #(cp/get-object-with-children % objects)) selected)
(map :id))
selected)
filter-shapes (fn [id] remove-snap? (mf/use-memo
(if (= id :layout) (mf/deps layout filter-shapes)
(or (not (contains? layout :display-grid)) #(snap/make-remove-snap layout filter-shapes))
(not (contains? layout :snap-grid)))
(or (filter-shapes id)
(not (contains? layout :dynamic-alignment)))))
shapes (if drawing [drawing] shapes)] shapes (if drawing [drawing] shapes)]
(when (or drawing transform) (when (or drawing transform)
[:& snap-feedback {:shapes shapes [:& snap-feedback {:shapes shapes
:page-id page-id :page-id page-id
:filter-shapes filter-shapes :remove-snap? remove-snap?
:zoom zoom :zoom zoom
:modifiers modifiers}]))) :modifiers modifiers}])))

View file

@ -515,6 +515,20 @@
(let [flows-node (get-data node :penpot:flows)] (let [flows-node (get-data node :penpot:flows)]
(->> flows-node :content (mapv parse-flow-node)))) (->> 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 (defn extract-from-data
([node tag] ([node tag]
(extract-from-data node tag identity)) (extract-from-data node tag identity))
@ -764,7 +778,8 @@
grids (->> (parse-grids node) grids (->> (parse-grids node)
(group-by :type) (group-by :type)
(d/mapm (fn [_ v] (-> v first :params)))) (d/mapm (fn [_ v] (-> v first :params))))
flows (parse-flows node)] flows (parse-flows node)
guides (parse-guides node)]
(cond-> {} (cond-> {}
(some? background) (some? background)
(assoc-in [:options :background] background) (assoc-in [:options :background] background)
@ -773,7 +788,10 @@
(assoc-in [:options :saved-grids] grids) (assoc-in [:options :saved-grids] grids)
(d/not-empty? flows) (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 (defn parse-interactions
[node] [node]

View file

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