♻️ Refactor page options data structure

This commit is contained in:
Andrey Antukh 2024-09-03 11:59:33 +02:00 committed by Alonso Torres
parent 2dea0b52ed
commit 4ad4057878
41 changed files with 659 additions and 525 deletions

View file

@ -65,7 +65,7 @@
(let [position (select-keys thread [:position :frame-id])]
(-> state
(update :comment-threads assoc id (dissoc thread :comment))
(update-in [:workspace-data :pages-index page-id :options :comment-threads-position] assoc id position)
(update-in [:workspace-data :pages-index page-id :comment-thread-positions] assoc id position)
(cond-> open?
(update :comments-local assoc :open id))
(update :comments-local assoc :options nil)
@ -122,7 +122,7 @@
(let [position (select-keys thread [:position :frame-id])]
(-> state
(update :comment-threads assoc id (dissoc thread :comment))
(update-in [:viewer :pages page-id :options :comment-threads-position] assoc id position)
(update-in [:viewer :pages page-id :comment-thread-positions] assoc id position)
(update :comments-local assoc :open id)
(update :comments-local assoc :options nil)
(update :comments-local dissoc :draft)
@ -273,7 +273,7 @@
(update [_ state]
(let [page-id (:current-page-id state)]
(-> state
(update-in [:workspace-data :pages-index page-id :options :comment-threads-position] dissoc id)
(update-in [:workspace-data :pages-index page-id :comment-thread-positions] dissoc id)
(update :comments dissoc id)
(update :comment-threads dissoc id))))
@ -299,7 +299,7 @@
(update [_ state]
(let [page-id (:current-page-id state)]
(-> state
(update-in [:viewer :pages page-id :options :comment-threads-position] dissoc id)
(update-in [:viewer :pages page-id :comment-thread-positions] dissoc id)
(update :comments dissoc id)
(update :comment-threads dissoc id))))
@ -356,7 +356,7 @@
[file-id]
(dm/assert! (uuid? file-id))
(letfn [(set-comment-threds [state comment-thread]
(let [path [:workspace-data :pages-index (:page-id comment-thread) :options :comment-threads-position (:id comment-thread)]
(let [path [:workspace-data :pages-index (:page-id comment-thread) :comment-thread-positions (:id comment-thread)]
thread-position (get-in state path)]
(cond-> state
(nil? thread-position)

View file

@ -568,7 +568,7 @@
(watch [it state _]
(let [page (get-in state [:workspace-data :pages-index id])
changes (-> (pcb/empty-changes it)
(pcb/mod-page page name))]
(pcb/mod-page page {:name name}))]
(rx/of (dch/commit-changes changes))))))
@ -2071,7 +2071,7 @@
page (wsh/lookup-page state page-id)
changes (-> (pcb/empty-changes it)
(pcb/with-page page)
(pcb/set-page-option :background (:color color)))]
(pcb/mod-page {:background (:color color)}))]
(rx/of (dch/commit-changes changes)))))))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

View file

@ -132,21 +132,20 @@
(ptk/reify ::update-comment-thread-position
ptk/WatchEvent
(watch [it state _]
(let [thread-id (:id thread)
page (wsh/lookup-page state)
page-id (:id page)
objects (wsh/lookup-page-objects state page-id)
new-frame-id (if (nil? frame-id)
(ctst/get-frame-id-by-position objects (gpt/point new-x new-y))
(:frame-id thread))
thread (assoc thread
:position (gpt/point new-x new-y)
:frame-id new-frame-id)
(let [page (wsh/lookup-page state)
page-id (:id page)
objects (wsh/lookup-page-objects state page-id)
frame-id (if (nil? frame-id)
(ctst/get-frame-id-by-position objects (gpt/point new-x new-y))
(:frame-id thread))
changes
(-> (pcb/empty-changes it)
(pcb/with-page page)
(pcb/update-page-option :comment-threads-position assoc thread-id (select-keys thread [:position :frame-id])))]
thread (-> thread
(assoc :position (gpt/point new-x new-y))
(assoc :frame-id frame-id))
changes (-> (pcb/empty-changes it)
(pcb/with-page page)
(pcb/set-comment-thread-position thread))]
(rx/merge
(rx/of (dch/commit-changes changes))
@ -164,25 +163,28 @@
(ptk/reify ::move-frame-comment-threads
ptk/WatchEvent
(watch [_ state _]
(let [objects (wsh/lookup-page-objects state)
(let [page (wsh/lookup-page state)
objects (get page :objects)
is-frame? (fn [id] (= :frame (get-in objects [id :type])))
is-frame? (fn [id] (= :frame (get-in objects [id :type])))
frame-ids? (into #{} (filter is-frame?) ids)
object-modifiers (:workspace-modifiers state)
threads-position-map
(get page :comment-thread-positions)
threads-position-map (:comment-threads-position (wsh/lookup-page-options state))
object-modifiers
(:workspace-modifiers state)
build-move-event
(fn [comment-thread]
(let [frame (get objects (:frame-id comment-thread))
(let [frame (get objects (:frame-id comment-thread))
modifiers (get-in object-modifiers [(:frame-id comment-thread) :modifiers])
frame' (gsh/transform-shape frame modifiers)
moved (gpt/to-vec (gpt/point (:x frame) (:y frame))
(gpt/point (:x frame') (:y frame')))
position (get-in threads-position-map [(:id comment-thread) :position])
new-x (+ (:x position) (:x moved))
new-y (+ (:y position) (:y moved))]
frame' (gsh/transform-shape frame modifiers)
moved (gpt/to-vec (gpt/point (:x frame) (:y frame))
(gpt/point (:x frame') (:y frame')))
position (get-in threads-position-map [(:id comment-thread) :position])
new-x (+ (:x position) (:x moved))
new-y (+ (:y position) (:y moved))]
(update-comment-thread-position comment-thread [new-x new-y] (:id frame))))]
(->> (:comment-threads state)

View file

@ -6,10 +6,10 @@
(ns app.main.data.workspace.grid
(:require
[app.common.colors :as clr]
[app.common.data :as d]
[app.common.data.macros :as dm]
[app.common.files.changes-builder :as pcb]
[app.common.types.grid :as ctg]
[app.main.data.changes :as dch]
[app.main.data.workspace.shapes :as dwsh]
[app.main.data.workspace.state-helpers :as wsh]
@ -20,25 +20,6 @@
;; Grid
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(defonce ^:private default-square-params
{:size 16
:color {:color clr/info
:opacity 0.4}})
(defonce ^:private default-layout-params
{:size 12
:type :stretch
:item-length nil
:gutter 8
:margin 0
:color {:color clr/default-layout
:opacity 0.1}})
(defonce default-grid-params
{:square default-square-params
:column default-layout-params
:row default-layout-params})
(defn add-frame-grid
[frame-id]
(dm/assert! (uuid? frame-id))
@ -46,9 +27,9 @@
ptk/WatchEvent
(watch [_ state _]
(let [page-id (:current-page-id state)
data (get-in state [:workspace-data :pages-index page-id])
params (or (get-in data [:options :saved-grids :square])
(:square default-grid-params))
page (dm/get-in state [:workspace-data :pages-index page-id])
params (or (dm/get-in page [:grids :square])
(:square ctg/default-grid-params))
grid {:type :square
:params params
:display true}]
@ -79,4 +60,4 @@
(rx/of (dch/commit-changes
(-> (pcb/empty-changes it)
(pcb/with-page page)
(pcb/set-page-option [:saved-grids type] params))))))))
(pcb/set-default-grid type params))))))))

View file

@ -17,18 +17,12 @@
[beicon.v2.core :as rx]
[potok.v2.core :as ptk]))
(defn make-update-guide
[guide]
(fn [other]
(cond-> other
(= (:id other) (:id guide))
(merge guide))))
(defn update-guides
[guide]
[{:keys [id] :as guide}]
(dm/assert!
"expected valid guide"
(ctp/check-page-guide! guide))
(ctp/valid-guide? guide))
(ptk/reify ::update-guides
ev/Event
@ -41,14 +35,15 @@
changes
(-> (pcb/empty-changes it)
(pcb/with-page page)
(pcb/update-page-option :guides assoc (:id guide) guide))]
(pcb/set-guide id guide))]
(rx/of (dwc/commit-changes changes))))))
(defn remove-guide
[guide]
[{:keys [id] :as guide}]
(dm/assert!
"expected valid guide"
(ctp/check-page-guide! guide))
(ctp/valid-guide? guide))
(ptk/reify ::remove-guide
ev/Event
@ -57,7 +52,7 @@
ptk/UpdateEvent
(update [_ state]
(let [sdisj (fnil disj #{})]
(update-in state [:workspace-guides :hover] sdisj (:id guide))))
(update-in state [:workspace-guides :hover] sdisj id)))
ptk/WatchEvent
(watch [it state _]
@ -65,18 +60,22 @@
changes
(-> (pcb/empty-changes it)
(pcb/with-page page)
(pcb/update-page-option :guides dissoc (:id guide)))]
(pcb/set-guide id nil))]
(rx/of (dwc/commit-changes changes))))))
(defn remove-guides
[ids]
(dm/assert!
"expected a set of ids"
(every? uuid? ids))
(ptk/reify ::remove-guides
ptk/WatchEvent
(watch [_ state _]
(let [page (wsh/lookup-page state)
guides (get-in page [:options :guides] {})
(let [{:keys [guides] :as page} (wsh/lookup-page state)
guides (-> (select-keys guides ids) (vals))]
(rx/from (->> guides (mapv #(remove-guide %))))))))
(rx/from (mapv remove-guide guides))))))
(defmethod ptk/resolve ::move-frame-guides
[_ args]
@ -105,7 +104,7 @@
guide (update guide :position + (get moved (:axis guide)))]
(update-guides guide)))
guides (-> state wsh/lookup-page-options :guides vals)]
guides (-> state wsh/lookup-page :guides vals)]
(->> guides
(filter (comp frame-ids? :frame-id))

View file

@ -43,18 +43,20 @@
(wsh/lookup-page state page-id)
(wsh/lookup-page state))
flows (get-in page [:options :flows] [])
unames (cfh/get-used-names flows)
flows (get page :flows)
unames (cfh/get-used-names (vals flows))
name (or name (cfh/generate-unique-name unames "Flow 1"))
new-flow {:id (or flow-id (uuid/next))
:name name
:starting-frame starting-frame}]
flow-id (or flow-id (uuid/next))
flow {:id flow-id
:name name
:starting-frame starting-frame}]
(rx/of (dch/commit-changes
(-> (pcb/empty-changes it)
(pcb/with-page page)
(pcb/update-page-option :flows ctp/add-flow new-flow)))))))))
(pcb/set-flow flow-id flow)))))))))
(defn add-flow-selected-frame
[]
@ -79,35 +81,40 @@
(rx/of (dch/commit-changes
(-> (pcb/empty-changes it)
(pcb/with-page page)
(pcb/update-page-option :flows ctp/remove-flow flow-id)))))))))
(pcb/set-flow flow-id nil)))))))))
(defn update-flow
[page-id flow-id update-fn]
(dm/assert! (uuid? flow-id))
(assert (uuid? flow-id) "expect valid flow-id")
(assert (uuid? page-id) "expect valid page-id")
(ptk/reify ::update-flow
ptk/WatchEvent
(watch [it state _]
(let [page (if page-id
(wsh/lookup-page state page-id)
(wsh/lookup-page state))]
(rx/of (dch/commit-changes
(-> (pcb/empty-changes it)
(pcb/with-page page)
(pcb/update-page-option :flows ctp/update-flow flow-id update-fn))))))))
(wsh/lookup-page state))
flow (dm/get-in page [:flows flow-id])
flow (some-> flow update-fn)]
(when (some? flow)
(rx/of (dch/commit-changes
(-> (pcb/empty-changes it)
(pcb/with-page page)
(pcb/set-flow flow-id flow)))))))))
(defn rename-flow
[flow-id name]
(dm/assert! (uuid? flow-id))
(dm/assert! (string? name))
(assert (uuid? flow-id) "expected valid flow-id")
(assert (string? name) "expected valid name")
(ptk/reify ::rename-flow
ptk/WatchEvent
(watch [it state _]
(watch [_ state _]
(let [page (wsh/lookup-page state)]
(rx/of (dch/commit-changes
(-> (pcb/empty-changes it)
(pcb/with-page page)
(pcb/update-page-option :flows ctp/update-flow flow-id
#(ctp/rename-flow % name)))))))))
(rx/of (update-flow (:id page) flow-id #(assoc % :name name)))))))
(defn start-rename-flow
[id]
@ -153,13 +160,11 @@
ptk/WatchEvent
(watch [_ state _]
(let [page-id (:current-page-id state)
objects (wsh/lookup-page-objects state page-id)
page (wsh/lookup-page state page-id)
objects (get page :objects)
frame (cfh/get-root-frame objects (:id shape))
flows (get-in state [:workspace-data
:pages-index
page-id
:options
:flows] [])
flows (get page :objects)
flow (ctp/get-frame-flow flows (:id frame))]
(rx/concat
(rx/of (dwsh/update-shapes [(:id shape)]

View file

@ -20,15 +20,12 @@
([state page-id]
(get-in state [:workspace-data :pages-index page-id])))
(defn lookup-data-objects
[data page-id]
(dm/get-in data [:pages-index page-id :objects]))
(defn lookup-page-objects
([state]
(lookup-page-objects state (:current-page-id state)))
([state page-id]
(dm/get-in state [:workspace-data :pages-index page-id :objects])))
(-> (lookup-page state page-id)
(get :objects))))
(defn lookup-viewer-objects
([state page-id]
@ -45,12 +42,6 @@
(lookup-page-objects state page-id)
(lookup-library-objects state file-id page-id))))
(defn lookup-page-options
([state]
(lookup-page-options state (:current-page-id state)))
([state page-id]
(dm/get-in state [:workspace-data :pages-index page-id :options])))
(defn lookup-local-components
([state]
(dm/get-in state [:workspace-data :components])))

View file

@ -182,6 +182,11 @@
[page-id [event [old-data new-data]]]
(let [changes (:changes event)
lookup-data-objects
(fn [data page-id]
(dm/get-in data [:pages-index page-id :objects]))
extract-ids
(fn [{:keys [page-id type] :as change}]
(case type
@ -193,8 +198,8 @@
get-frame-ids
(fn get-frame-ids [id]
(let [old-objects (wsh/lookup-data-objects old-data page-id)
new-objects (wsh/lookup-data-objects new-data page-id)
(let [old-objects (lookup-data-objects old-data page-id)
new-objects (lookup-data-objects new-data page-id)
new-shape (get new-objects id)
old-shape (get old-objects id)

View file

@ -281,6 +281,9 @@
(dm/get-in data [:pages-index page-id])))
st/state))
(def workspace-page-flows
(l/derived #(-> % :flows not-empty) workspace-page))
(defn workspace-page-objects-by-id
[page-id]
(l/derived #(wsh/lookup-page-objects % page-id) st/state =))
@ -343,9 +346,6 @@
(into [] (keep (d/getf objects)) children-ids)))
workspace-page-objects =))
(def workspace-page-options
(l/derived :options workspace-page))
(def workspace-frames
(l/derived ctt/get-frames workspace-page-objects =))

View file

@ -211,7 +211,7 @@
shapes (cfh/get-immediate-children objects)
dim (calculate-dimensions objects aspect-ratio)
vbox (format-viewbox dim)
bgcolor (dm/get-in data [:options :background] default-color)
bgcolor (get data :background default-color)
shape-wrapper
(mf/use-memo
@ -232,7 +232,7 @@
:fill "none"}
(when include-metadata
[:& export/export-page {:id (:id data) :options (:options data)}])
[:& export/export-page {:page data}])
(let [shapes (->> shapes
(remove cfh/frame-shape?)

View file

@ -187,14 +187,16 @@
:axis (d/name axis)}])])
(mf/defc export-page
[{:keys [id options]}]
(let [saved-grids (get options :saved-grids)
flows (get options :flows)
guides (get options :guides)]
{::mf/props :obj}
[{:keys [page]}]
(let [id (get page :id)
grids (get page :grids)
flows (get page :flows)
guides (get page :guides)]
[:> "penpot:page" #js {:id id}
(when (d/not-empty? saved-grids)
(when (d/not-empty? grids)
(let [parse-grid (fn [[type params]] {:type type :params params})
grids (->> saved-grids (mapv parse-grid))]
grids (mapv parse-grid grids)]
[:& export-grid-data {:grids grids}]))
(when (d/not-empty? flows)

View file

@ -139,7 +139,7 @@
:viewport-size)
tpos-ref (mf/with-memo [page-id]
(-> (l/in [:pages page-id :options :comment-threads-position])
(-> (l/in [:pages page-id :comment-thread-positions])
(l/derived refs/viewer-data)))
positions (mf/deref tpos-ref)

View file

@ -221,13 +221,14 @@
(mf/defc flows-menu
{::mf/wrap [mf/memo]}
[{:keys [page index]}]
(let [flows (dm/get-in page [:options :flows])
frames (:frames page)
frame (get frames index)
current-flow* (mf/use-state
#(ctp/get-frame-flow flows (:id frame)))
(let [flows (:flows page)
frames (:frames page)
current-flow (deref current-flow*)
frame (get frames index)
current-flow* (mf/use-state
#(ctp/get-frame-flow flows (:id frame)))
current-flow (deref current-flow*)
show-dropdown?* (mf/use-state false)
show-dropdown? (deref show-dropdown?*)

View file

@ -373,26 +373,25 @@
:on-click do-lock-shape}])]))
(mf/defc context-menu-prototype
{::mf/props :obj}
[{:keys [shapes]}]
(let [options (mf/deref refs/workspace-page-options)
(let [flows (mf/deref refs/workspace-page-flows)
options-mode (mf/deref refs/options-mode-global)
do-add-flow #(st/emit! (dwi/add-flow-selected-frame))
do-remove-flow #(st/emit! (dwi/remove-flow (:id %)))
flows (:flows options)
prototype? (= options-mode :prototype)
single? (= (count shapes) 1)
has-frame? (->> shapes (d/seek cfh/frame-shape?))
has-frame? (d/seek cfh/frame-shape? shapes)
is-frame? (and single? has-frame?)]
(when (and prototype? is-frame?)
(let [flow (ctp/get-frame-flow flows (-> shapes first :id))]
(if (some? flow)
[:& menu-entry {:title (tr "workspace.shape.menu.delete-flow-start")
:on-click (do-remove-flow flow)}]
[:& menu-entry {:title (tr "workspace.shape.menu.flow-start")
:on-click do-add-flow}])))))
(if-let [flow (ctp/get-frame-flow flows (-> shapes first :id))]
[:& menu-entry {:title (tr "workspace.shape.menu.delete-flow-start")
:on-click (do-remove-flow flow)}]
[:& menu-entry {:title (tr "workspace.shape.menu.flow-start")
:on-click do-add-flow}]))))
(mf/defc context-menu-layout
{::mf/props :obj}

View file

@ -8,6 +8,7 @@
(:require-macros [app.main.style :as stl])
(:require
[app.common.geom.grid :as gg]
[app.common.types.grid :as ctg]
[app.main.data.workspace.grid :as dw]
[app.main.refs :as refs]
[app.main.store :as st]
@ -22,8 +23,8 @@
[okulary.core :as l]
[rumext.v2 :as mf]))
(def workspace-saved-grids
(l/derived :saved-grids refs/workspace-page-options))
(def lens:default-grids
(l/derived :default-grids refs/workspace-data))
(defn- get-size-options []
[{:value nil :label (tr "workspace.options.grid.auto")}
@ -297,10 +298,16 @@
has-frame-grids? (or (= :multiple frame-grids) (some? (seq frame-grids)))
toggle-content (mf/use-fn #(swap! state* not))
id (:id shape)
saved-grids (mf/deref workspace-saved-grids)
default-grid-params (mf/use-memo (mf/deps saved-grids) #(merge dw/default-grid-params saved-grids))
handle-create-grid (mf/use-fn (mf/deps id) #(st/emit! (dw/add-frame-grid id)))]
default-grids (mf/deref lens:default-grids)
default-grid-params (mf/with-memo [default-grids]
(merge ctg/default-grid-params default-grids))
handle-create-grid
(mf/use-fn
(mf/deps id)
#(st/emit! (dw/add-frame-grid id)))]
[:div {:class (stl/css :element-set)}
[:& title-bar {:collapsable has-frame-grids?

View file

@ -154,19 +154,21 @@
i/remove-icon]]))
(mf/defc page-flows
{::mf/props :obj}
[{:keys [flows]}]
(when (seq flows)
(when flows
[:div {:class (stl/css :interaction-options)}
[:& title-bar {:collapsable false
:title (tr "workspace.options.flows.flow-starts")
:class (stl/css :title-spacing-layout-flow)}]
(for [flow flows]
[:& flow-item {:flow flow :key (str (:id flow))}])]))
(for [[id flow] flows]
[:& flow-item {:flow flow :key (dm/str id)}])]))
(mf/defc shape-flows
{::mf/props :obj}
[{:keys [flows shape]}]
(when (= (:type shape) :frame)
(let [flow (ctp/get-frame-flow flows (:id shape))
(when (cfh/frame-shape? shape)
(let [flow (ctp/get-frame-flow flows (:id shape))
add-flow (mf/use-fn #(st/emit! (dwi/add-flow-selected-frame)))]
[:div {:class (stl/css :element-set)}
@ -179,8 +181,8 @@
:on-click add-flow}
i/add])]
(when flow
[:& flow-item {:flow flow :key (str (:id flow))}])])))
(when (some? flow)
[:& flow-item {:flow flow :key (dm/str (:id flow))}])])))
(def ^:private corner-center-icon
(i/icon-xref :corner-center (stl/css :corner-icon)))
@ -695,11 +697,12 @@
:on-change change-offset-effect}]]]])])])]))
(mf/defc interactions-menu
[{:keys [shape] :as props}]
(let [interactions (get shape :interactions [])
{::mf/props :obj}
[{:keys [shape]}]
(let [interactions
(get shape :interactions [])
options (mf/deref refs/workspace-page-options)
flows (:flows options)
flows (mf/deref refs/workspace-page-flows)
add-interaction
(fn []

View file

@ -9,6 +9,7 @@
(:require-macros [app.main.style :as stl])
(:require
[app.common.colors :as clr]
[app.common.data :as d]
[app.main.data.workspace :as dw]
[app.main.data.workspace.undo :as dwu]
[app.main.refs :as refs]
@ -16,16 +17,21 @@
[app.main.ui.components.title-bar :refer [title-bar]]
[app.main.ui.workspace.sidebar.options.rows.color-row :refer [color-row]]
[app.util.i18n :as i18n :refer [tr]]
[okulary.core :as l]
[rumext.v2 :as mf]))
(def lens:background-color
(-> (l/key :background)
(l/derived refs/workspace-page)))
(mf/defc options
{::mf/wrap [mf/memo]
::mf/wrap-props false}
[]
(let [options (mf/deref refs/workspace-page-options)
on-change (mf/use-fn #(st/emit! (dw/change-canvas-color %)))
on-open (mf/use-fn #(st/emit! (dwu/start-undo-transaction :options)))
on-close (mf/use-fn #(st/emit! (dwu/commit-undo-transaction :options)))]
(let [background (mf/deref lens:background-color)
on-change (mf/use-fn #(st/emit! (dw/change-canvas-color %)))
on-open (mf/use-fn #(st/emit! (dwu/start-undo-transaction :options)))
on-close (mf/use-fn #(st/emit! (dwu/commit-undo-transaction :options)))]
[:div {:class (stl/css :element-set)}
[:div {:class (stl/css :element-title)}
[:& title-bar {:collapsable false
@ -37,7 +43,7 @@
:disable-opacity true
:disable-image true
:title (tr "workspace.options.canvas-background")
:color {:color (get options :background clr/canvas)
:color {:color (d/nilv background clr/canvas)
:opacity 1}
:on-change on-change
:on-open on-open

View file

@ -92,28 +92,24 @@
vbox' (mf/use-debounce 100 vbox)
;; CONTEXT
page-id (mf/use-ctx ctx/current-page-id)
;; DEREFS
drawing (mf/deref refs/workspace-drawing)
options (mf/deref refs/workspace-page-options)
focus (mf/deref refs/workspace-focus-selected)
objects-ref (mf/use-memo #(refs/workspace-page-objects-by-id page-id))
objects (mf/deref objects-ref)
base-objects (-> objects (ui-hooks/with-focus-objects focus))
page (mf/deref refs/workspace-page)
objects (get page :objects)
page-id (get page :id)
background (get page :background clr/canvas)
base-objects (ui-hooks/with-focus-objects objects focus)
modifiers (mf/deref refs/workspace-modifiers)
text-modifiers (mf/deref refs/workspace-text-modifier)
objects-modified (mf/with-memo
[base-objects text-modifiers modifiers]
objects-modified (mf/with-memo [base-objects text-modifiers modifiers]
(apply-modifiers-to-selected selected base-objects text-modifiers modifiers))
selected-shapes (->> selected (keep (d/getf objects-modified)))
background (get options :background clr/canvas)
selected-shapes (keep (d/getf objects-modified) selected)
;; STATE
alt? (mf/use-state false)
@ -305,7 +301,6 @@
(when picking-color?
[:& pixel-overlay/pixel-overlay {:vport vport
:vbox vbox
:options options
:layout layout
:viewport-ref viewport-ref}])]
@ -338,7 +333,7 @@
[:stop {:offset "100%" :stop-color (str "color-mix(in srgb-linear, " background " 90%, #777)") :stop-opacity 1}]]]
(when (dbg/enabled? :show-export-metadata)
[:& use/export-page {:options options}])
[:& use/export-page {:page page}])
;; We need a "real" background shape so layer transforms work properly in firefox
[:rect {:width (:width vbox 0)
@ -486,7 +481,7 @@
(when show-prototypes?
[:& widgets/frame-flows
{:flows (:flows options)
{:flows (:flows page)
:objects objects-modified
:selected selected
:zoom zoom
@ -556,11 +551,11 @@
:show-rulers? show-rulers?}])
(when (and show-rulers? show-grids?)
[:& guides/viewport-guides
[:> guides/viewport-guides*
{:zoom zoom
:vbox vbox
:hover-frame guide-frame
:disabled-guides? disabled-guides?
:disabled-guides disabled-guides?
:modifiers modifiers}])
;; DEBUG LAYOUT DROP-ZONES

View file

@ -41,7 +41,8 @@
positions-ref
(mf/with-memo [page-id]
(-> (l/in [:workspace-data :pages-index page-id :options :comment-threads-position])
;; FIXME: use lookup helpers here
(-> (l/in [:workspace-data :pages-index page-id :comment-thread-positions])
(l/derived st/state)))
positions (mf/deref positions-ref)

View file

@ -7,6 +7,7 @@
(ns app.main.ui.workspace.viewport.guides
(:require
[app.common.colors :as colors]
[app.common.data.macros :as dm]
[app.common.files.helpers :as cfh]
[app.common.geom.point :as gpt]
[app.common.geom.shapes :as gsh]
@ -21,6 +22,7 @@
[app.main.ui.formats :as fmt]
[app.main.ui.workspace.viewport.rulers :as rulers]
[app.util.dom :as dom]
[okulary.core :as l]
[rumext.v2 :as mf]))
(def guide-width 1)
@ -256,14 +258,15 @@
(and (>= (:position guide) (:y frame))
(<= (:position guide) (+ (:y frame) (:height frame))))))
(mf/defc guide
{::mf/wrap [mf/memo]}
[{:keys [guide hover? on-guide-change get-hover-frame vbox zoom hover-frame disabled-guides? frame-modifier]}]
(mf/defc guide*
{::mf/wrap [mf/memo]
::mf/props :obj}
[{:keys [guide is-hover on-guide-change get-hover-frame vbox zoom hover-frame disabled-guides frame-modifier]}]
(let [axis (:axis guide)
handle-change-position
(mf/use-callback
(mf/use-fn
(mf/deps on-guide-change)
(fn [changes]
(when on-guide-change
@ -296,7 +299,7 @@
(and (cfh/root-frame? frame)
(not (ctst/rotated-frame? frame))))
[:g.guide-area {:opacity (when frame-guide-outside? 0)}
(when-not disabled-guides?
(when-not disabled-guides
(let [{:keys [x y width height]} (guide-area-axis pos vbox zoom frame axis)]
[:rect {:x x
:y y
@ -318,7 +321,7 @@
l3-x1 l3-y1 l3-x2 l3-y2]}
(guide-line-axis pos vbox frame axis)]
[:g
(when (or hover? (:hover @state))
(when (or is-hover (:hover @state))
[:line {:x1 l1-x1
:y1 l1-y1
:x2 l1-x2
@ -334,10 +337,10 @@
:y2 l2-y2
:style {:stroke guide-color
:stroke-width guide-width
:stroke-opacity (if (or hover? (:hover @state))
:stroke-opacity (if (or is-hover (:hover @state))
guide-opacity-hover
guide-opacity)}}]
(when (or hover? (:hover @state))
(when (or is-hover (:hover @state))
[:line {:x1 l3-x1
:y1 l3-y1
:x2 l3-x2
@ -355,11 +358,11 @@
:y2 y2
:style {:stroke guide-color
:stroke-width guide-width
:stroke-opacity (if (or hover? (:hover @state))
:stroke-opacity (if (or is-hover (:hover @state))
guide-opacity-hover
guide-opacity)}}]))
(when (or hover? (:hover @state))
(when (or is-hover (:hover @state))
(let [{:keys [rect-x rect-y rect-width rect-height text-x text-y]}
(guide-pill-axis pos vbox zoom axis)]
[:g.guide-pill
@ -382,16 +385,17 @@
;; If the guide is associated to a frame we show the position relative to the frame
(fmt/format-number (- pos (if (= axis :x) (:x frame) (:y frame))))]]))])))
(mf/defc new-guide-area
[{:keys [vbox zoom axis get-hover-frame disabled-guides?]}]
(mf/defc new-guide-area*
{::mf/props :obj}
[{:keys [vbox zoom axis get-hover-frame disabled-guides]}]
(let [on-guide-change
(mf/use-callback
(mf/use-fn
(mf/deps vbox)
(fn [guide]
(let [guide (-> guide
(assoc :id (uuid/next)
:axis axis))]
(assoc :id (uuid/next))
(assoc :axis axis))]
(when (guide-inside-vbox? zoom vbox guide)
(st/emit! (dw/update-guides guide))))))
@ -402,10 +406,11 @@
on-lost-pointer-capture
on-pointer-move
state
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
(when-not disabled-guides?
(when-not disabled-guides
(let [{:keys [x y width height]} (guide-creation-area vbox zoom axis)]
[:rect {:x x
:y y
@ -422,25 +427,25 @@
:pointer-events "fill"}}]))
(when (:new-position @state)
[:& guide {:guide {:axis axis
:position (:new-position @state)}
:get-hover-frame get-hover-frame
:vbox vbox
:zoom zoom
:hover? true
:hover-frame frame}])]))
[:& guide* {:guide {:axis axis :position (:new-position @state)}
:get-hover-frame get-hover-frame
:vbox vbox
:zoom zoom
:is-hover true
:hover-frame frame}])]))
(mf/defc viewport-guides
{::mf/wrap [mf/memo]}
[{:keys [zoom vbox hover-frame disabled-guides? modifiers]}]
(def ^:private lens:workspace-guides
(-> (l/key :guides)
(l/derived refs/workspace-page)))
(let [page (mf/deref refs/workspace-page)
guides (mf/use-memo
(mf/deps page vbox)
#(->> (get-in page [:options :guides] {})
(vals)
(filter (guide-inside-vbox? zoom vbox))))
(mf/defc viewport-guides*
{::mf/wrap [mf/memo]
::mf/props :obj}
[{:keys [zoom vbox hover-frame disabled-guides modifiers]}]
(let [guides (mf/deref lens:workspace-guides)
guides (mf/with-memo [guides vbox]
(->> (vals guides)
(filter (partial guide-inside-vbox? zoom vbox))))
focus (mf/deref refs/workspace-focus-selected)
@ -449,46 +454,42 @@
;; We use the ref to not redraw every guide everytime the hovering frame change
;; we're only interested to get the frame in the guide we're moving
get-hover-frame
(mf/use-callback
(fn []
(mf/ref-val hover-frame-ref)))
(mf/use-fn
#(mf/ref-val hover-frame-ref))
on-guide-change
(mf/use-callback
(mf/use-fn
(mf/deps vbox)
(fn [guide]
(if (guide-inside-vbox? zoom vbox guide)
(st/emit! (dw/update-guides guide))
(st/emit! (dw/remove-guide guide)))))]
(mf/use-effect
(mf/deps hover-frame)
(fn []
(mf/set-ref-val! hover-frame-ref hover-frame)))
(mf/with-effect [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
:disabled-guides? disabled-guides?}]
[:> 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?}]
[:> new-guide-area* {:vbox vbox
:zoom zoom
:axis :y
:get-hover-frame get-hover-frame
:disabled-guides disabled-guides}]
(for [current guides]
(when (or (nil? (:frame-id current))
(empty? focus)
(contains? focus (:frame-id current)))
[:& guide {:key (str "guide-" (:id current))
[:> guide* {:key (dm/str "guide-" (:id current))
:guide current
:vbox vbox
:zoom zoom
:frame-modifier (get-in modifiers [(:frame-id current) :modifiers])
:frame-modifier (dm/get-in modifiers [(:frame-id current) :modifiers])
:get-hover-frame get-hover-frame
:on-guide-change on-guide-change
:disabled-guides? disabled-guides?}]))]))
:disabled-guides disabled-guides}]))]))

View file

@ -53,10 +53,10 @@
(mf/defc pixel-overlay
{::mf/wrap-props false}
[props]
(let [vport (unchecked-get props "vport")
(let [vport (unchecked-get props "vport")
viewport-ref (unchecked-get props "viewport-ref")
viewport-node (mf/ref-val viewport-ref)
viewport-ref (unchecked-get props "viewport-ref")
viewport-node (mf/ref-val viewport-ref)
canvas (get-offscreen-canvas (:width vport) (:height vport))
canvas-context (.getContext canvas "2d" #js {:willReadFrequently true})

View file

@ -282,6 +282,7 @@
on-frame-leave (unchecked-get props "on-frame-leave")
on-frame-select (unchecked-get props "on-frame-select")]
[:g.frame-flows
;; FIXME: enumerate is not necessary here
(for [[index flow] (d/enumerate flows)]
(let [frame (get objects (:starting-frame flow))]
[:& frame-flow {:key (dm/str (:id frame) "-" index)

View file

@ -126,7 +126,7 @@
:else
(let [page (u/proxy->page self)]
(dm/get-in page [:options :plugin-data (keyword "plugin" (str $plugin)) key]))))
(dm/get-in page [:plugin-data (keyword "plugin" (str $plugin)) key]))))
(setPluginData
[_ key value]
@ -146,7 +146,7 @@
(getPluginDataKeys
[self]
(let [page (u/proxy->page self)]
(apply array (keys (dm/get-in page [:options :plugin-data (keyword "plugin" (str $plugin))])))))
(apply array (keys (dm/get-in page [:plugin-data (keyword "plugin" (str $plugin))])))))
(getSharedPluginData
[self namespace key]
@ -159,7 +159,7 @@
:else
(let [page (u/proxy->page self)]
(dm/get-in page [:options :plugin-data (keyword "shared" namespace) key]))))
(dm/get-in page [:plugin-data (keyword "shared" namespace) key]))))
(setSharedPluginData
[_ namespace key value]
@ -188,7 +188,7 @@
:else
(let [page (u/proxy->page self)]
(apply array (keys (dm/get-in page [:options :plugin-data (keyword "shared" namespace)]))))))
(apply array (keys (dm/get-in page [:plugin-data (keyword "shared" namespace)]))))))
(openPage
[_]
@ -385,7 +385,7 @@
{:name "background"
:enumerable false
:get #(or (-> % u/proxy->page :options :background) cc/canvas)
:get #(or (-> % u/proxy->page :background) cc/canvas)
:set
(fn [_ value]
(cond
@ -401,13 +401,13 @@
{:name "flows"
:get
(fn [self]
(let [flows (d/nilv (-> (u/proxy->page self) :options :flows) [])]
(let [flows (d/nilv (-> (u/proxy->page self) :flows) [])]
(format/format-array #(flow-proxy plugin-id file-id id (:id %)) flows)))}
{:name "rulerGuides"
:get
(fn [self]
(let [guides (-> (u/proxy->page self) :options :guides)]
(let [guides (-> (u/proxy->page self) :guides)]
(->> guides
(vals)
(filter #(nil? (:frame-id %)))

View file

@ -120,7 +120,7 @@
flow-id (obj/get proxy "$id")
page (locate-page file-id page-id)]
(when (some? page)
(d/seek #(= (:id %) flow-id) (-> page :options :flows)))))
(d/seek #(= (:id %) flow-id) (:flows page)))))
(defn proxy->ruler-guide
[proxy]

View file

@ -211,11 +211,11 @@
(defn add-page
"Adds page information"
[snap-data {:keys [objects options] :as page}]
[snap-data {:keys [objects guides] :as page}]
(let [frames (ctst/get-frames objects)
shapes (->> (vals (:objects page))
(remove cfh/frame-shape?))
guides (vals (:guides options))
guides (vals guides)
page-data
(as-> {} $

View file

@ -452,22 +452,28 @@
(defn import-page
[context file [page-id page-name content]]
(let [nodes (->> content parser/node-seq)
file-id (:id file)
resolve (:resolve context)
(let [nodes (parser/node-seq content)
file-id (:id file)
resolve (:resolve context)
page-data (-> (parser/parse-page-data content)
(assoc :name page-name)
(assoc :id (resolve page-id)))
flows (->> (get-in page-data [:options :flows])
(mapv #(update % :starting-frame resolve)))
flows (->> (get page-data :flows)
(update-vals #(update % :starting-frame resolve))
(not-empty))
guides (-> (get-in page-data [:options :guides])
(update-vals #(update % :frame-id resolve)))
guides (-> (get page-data :guides)
(update-vals #(update % :frame-id resolve))
(not-empty))
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))
page-data (cond-> page-data
flows
(assoc :flows flows)
guides
(assoc :guides guides))
file (fb/add-page file page-data)
;; Preprocess nodes to parallel upload the images. Store the result in a table
;; old-node => node with image

View file

@ -1136,16 +1136,16 @@
guides (parse-guides node)]
(cond-> {}
(some? background)
(assoc-in [:options :background] background)
(assoc :background background)
(d/not-empty? grids)
(assoc-in [:options :saved-grids] grids)
(assoc :default-grids grids)
(d/not-empty? flows)
(assoc-in [:options :flows] flows)
(assoc :flows flows)
(d/not-empty? guides)
(assoc-in [:options :guides] guides))))
(assoc :guides guides))))
(defn parse-interactions
[node]

View file

@ -7,7 +7,6 @@
(ns app.worker.thumbnails
(:require
["react-dom/server" :as rds]
[app.common.data.macros :as dm]
[app.common.logging :as log]
[app.common.uri :as u]
[app.config :as cf]
@ -63,7 +62,7 @@
(binding [fonts/loaded-hints (l/atom #{})]
(let [objects (:objects page)
frame (some->> page :thumbnail-frame-id (get objects))
background-color (dm/get-in page [:options :background])
background-color (:background page)
element (if frame
(mf/element render/frame-svg #js
{:objects objects