♻️ 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

@ -741,46 +741,36 @@
(defn add-guide (defn add-guide
[file guide] [file guide]
(let [guide (cond-> guide (let [guide (cond-> guide
(nil? (:id guide)) (nil? (:id guide))
(assoc :id (uuid/next))) (assoc :id (uuid/next)))
page-id (:current-page-id file) page-id (:current-page-id file)]
old-guides (or (dm/get-in file [:data :pages-index page-id :options :guides]) {})
new-guides (assoc old-guides (:id guide) guide)]
(-> file (-> file
(commit-change (commit-change
{:type :set-option {:type :set-guide
:page-id page-id :page-id page-id
:option :guides :id (:id guide)
:value new-guides}) :params guide})
(assoc :last-id (:id guide))))) (assoc :last-id (:id guide)))))
(defn delete-guide (defn delete-guide
[file id] [file id]
(let [page-id (:current-page-id file) (let [page-id (:current-page-id file)]
old-guides (or (dm/get-in file [:data :pages-index page-id :options :guides]) {}) (commit-change file
new-guides (dissoc old-guides id)] {:type :set-guide
(-> file :page-id page-id
(commit-change :id id
{:type :set-option :params nil})))
:page-id page-id
:option :guides
:value new-guides}))))
(defn update-guide (defn update-guide
[file guide] [file guide]
(let [page-id (:current-page-id file)]
(let [page-id (:current-page-id file) (commit-change file
old-guides (or (dm/get-in file [:data :pages-index page-id :options :guides]) {}) {:type :set-guide
new-guides (assoc old-guides (:id guide) guide)] :page-id page-id
(-> file :id (:id guide)
(commit-change :params guide})))
{:type :set-option
:page-id page-id
:option :guides
:value new-guides}))))
(defn strip-image-extension [filename] (defn strip-image-extension [filename]
(let [image-extensions-re #"(\.png)|(\.jpg)|(\.jpeg)|(\.webp)|(\.gif)|(\.svg)$"] (let [image-extensions-re #"(\.png)|(\.jpg)|(\.jpeg)|(\.webp)|(\.gif)|(\.svg)$"]

View file

@ -10,21 +10,25 @@
[app.common.data.macros :as dm] [app.common.data.macros :as dm]
[app.common.exceptions :as ex] [app.common.exceptions :as ex]
[app.common.files.helpers :as cfh] [app.common.files.helpers :as cfh]
[app.common.geom.point :as gpt]
[app.common.geom.shapes :as gsh] [app.common.geom.shapes :as gsh]
[app.common.schema :as sm] [app.common.schema :as sm]
[app.common.schema.desc-native :as smd] [app.common.schema.desc-native :as smd]
[app.common.schema.generators :as sg]
[app.common.types.color :as ctc] [app.common.types.color :as ctc]
[app.common.types.colors-list :as ctcl] [app.common.types.colors-list :as ctcl]
[app.common.types.component :as ctk] [app.common.types.component :as ctk]
[app.common.types.components-list :as ctkl] [app.common.types.components-list :as ctkl]
[app.common.types.container :as ctn] [app.common.types.container :as ctn]
[app.common.types.file :as ctf] [app.common.types.file :as ctf]
[app.common.types.grid :as ctg]
[app.common.types.page :as ctp] [app.common.types.page :as ctp]
[app.common.types.pages-list :as ctpl] [app.common.types.pages-list :as ctpl]
[app.common.types.shape :as cts] [app.common.types.shape :as cts]
[app.common.types.shape-tree :as ctst] [app.common.types.shape-tree :as ctst]
[app.common.types.typographies-list :as ctyl] [app.common.types.typographies-list :as ctyl]
[app.common.types.typography :as ctt] [app.common.types.typography :as ctt]
[app.common.uuid :as uuid]
[clojure.set :as set])) [clojure.set :as set]))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
@ -60,6 +64,44 @@
[:type [:= :set-remote-synced]] [:type [:= :set-remote-synced]]
[:remote-synced {:optional true} [:maybe :boolean]]]]]) [:remote-synced {:optional true} [:maybe :boolean]]]]])
(defn- set-default-grid-change-generator
[]
(->> (sg/elements #{:square :column :row})
(sg/mcat (fn [type]
(sg/fmap (fn [params]
{:page-id (uuid/next)
:type :mod-grid
:grid-type type
:params params})
(case type
:square (sg/generator ctg/schema:square-params)
:column (sg/generator ctg/schema:column-params)
:row (sg/generator ctg/schema:column-params)))))))
(def schema:set-default-grid-change
[:multi {:decode/json #(update % :grid-type keyword)
:gen/gen (set-default-grid-change-generator)
:dispatch :grid-type
::smd/simplified true}
[:square
[:map
[:page-id ::sm/uuid]
[:grid-type [:and :keyword [:= :square]]]
[:params [:maybe ctg/schema:square-params]]]]
[:column
[:map
[:page-id ::sm/uuid]
[:grid-type [:and :keyword [:= :column]]]
[:params [:maybe ctg/schema:column-params]]]]
[:row
[:map
[:page-id ::sm/uuid]
[:grid-type [:and :keyword [:= :row]]]
[:params [:maybe ctg/schema:column-params]]]]])
(def schema:change (def schema:change
[:schema [:schema
[:multi {:dispatch :type [:multi {:dispatch :type
@ -67,13 +109,18 @@
:decode/json #(update % :type keyword) :decode/json #(update % :type keyword)
::smd/simplified true} ::smd/simplified true}
[:set-option [:set-option
[:map {:title "SetOptionChange"}
[:type [:= :set-option]] ;; DEPRECATED: remove before 2.3 release
;;
;; Is still there for not cause error when event is received
[:map {:title "SetOptionChange"}]]
[:set-comment-thread-position
[:map
[:comment-thread-id ::sm/uuid]
[:page-id ::sm/uuid] [:page-id ::sm/uuid]
[:option [:union [:frame-id [:maybe ::sm/uuid]]
[:keyword] [:position [:maybe ::gpt/point]]]]
[:vector {:gen/max 10} :keyword]]]
[:value :any]]]
[:add-obj [:add-obj
[:map {:title "AddObjChange"} [:map {:title "AddObjChange"}
@ -103,6 +150,22 @@
[:component-id {:optional true} ::sm/uuid] [:component-id {:optional true} ::sm/uuid]
[:ignore-touched {:optional true} :boolean]]] [:ignore-touched {:optional true} :boolean]]]
[:set-guide
[:map {:title "SetGuideChange"}
[:page-id ::sm/uuid]
[:id ::sm/uuid]
[:params [:maybe ::ctp/guide]]]]
[:set-flow
[:map {:title "SetFlowChange"}
[:page-id ::sm/uuid]
[:id ::sm/uuid]
[:params [:maybe ::ctp/flow]]]]
;; Change used for all crud operation on persisted grid options on
;; the page.
[:set-default-grid schema:set-default-grid-change]
[:fix-obj [:fix-obj
[:map {:title "FixObjChange"} [:map {:title "FixObjChange"}
[:type [:= :fix-obj]] [:type [:= :fix-obj]]
@ -143,7 +206,10 @@
[:map {:title "ModPageChange"} [:map {:title "ModPageChange"}
[:type [:= :mod-page]] [:type [:= :mod-page]]
[:id ::sm/uuid] [:id ::sm/uuid]
[:name :string]]] ;; All props are optional, background can be nil because is the
;; way to remove already set background
[:background {:optional true} [:maybe ::ctc/rgb-color]]
[:name {:optional true} :string]]]
[:mod-plugin-data [:mod-plugin-data
[:map {:title "ModPagePluginData"} [:map {:title "ModPagePluginData"}
@ -356,14 +422,49 @@
#?(:clj (validate-shapes! data result items)) #?(:clj (validate-shapes! data result items))
result)))) result))))
;; DEPRECATED: remove before 2.3 release
(defmethod process-change :set-option (defmethod process-change :set-option
[data {:keys [page-id option value]}] [data _]
data)
;; --- Comment Threads
(defmethod process-change :set-comment-thread-position
[data {:keys [page-id comment-thread-id position frame-id]}]
(d/update-in-when data [:pages-index page-id] (d/update-in-when data [:pages-index page-id]
(fn [data] (fn [page]
(let [path (if (seqable? option) option [option])] (if (and position frame-id)
(if value (update page :comment-thread-positions assoc
(assoc-in data (into [:options] path) value) comment-thread-id {:frame-id frame-id
(assoc data :options (d/dissoc-in (:options data) path))))))) :position position})
(update page :comment-thread-positions dissoc
comment-thread-id)))))
;; --- Guides
(defmethod process-change :set-guide
[data {:keys [page-id id params]}]
(if (nil? params)
(d/update-in-when data [:pages-index page-id] update :guides dissoc id)
(d/update-in-when data [:pages-index page-id] update :guides assoc id params)))
;; --- Flows
(defmethod process-change :set-flow
[data {:keys [page-id id params]}]
(if (nil? params)
(d/update-in-when data [:pages-index page-id] update :flows dissoc id)
(d/update-in-when data [:pages-index page-id] update :flows assoc id params)))
;; --- Grids
(defmethod process-change :set-default-grid
[data {:keys [page-id grid-type params]}]
(if (nil? params)
(d/update-in-when data [:pages-index page-id] update :default-grids dissoc grid-type)
(d/update-in-when data [:pages-index page-id] update :default-grids assoc grid-type params)))
;; --- Shape / Obj
(defmethod process-change :add-obj (defmethod process-change :add-obj
[data {:keys [id obj page-id component-id frame-id parent-id index ignore-touched]}] [data {:keys [id obj page-id component-id frame-id parent-id index ignore-touched]}]
@ -603,8 +704,20 @@
(ctpl/add-page data page))) (ctpl/add-page data page)))
(defmethod process-change :mod-page (defmethod process-change :mod-page
[data {:keys [id name]}] [data {:keys [id] :as params}]
(d/update-in-when data [:pages-index id] assoc :name name)) (d/update-in-when data [:pages-index id]
(fn [page]
(let [name (get params :name)
bg (get params :background :not-found)]
(cond-> page
(string? name)
(assoc :name name)
(string? bg)
(assoc :background bg)
(nil? bg)
(dissoc :background))))))
(defmethod process-change :mod-plugin-data (defmethod process-change :mod-plugin-data
[data {:keys [object-type object-id page-id namespace key value]}] [data {:keys [object-type object-id page-id namespace key value]}]
@ -660,6 +773,7 @@
[data _] [data _]
data) data)
;; -- Media ;; -- Media
(defmethod process-change :add-media (defmethod process-change :add-media

View file

@ -135,12 +135,6 @@
(or (contains? (meta changes) ::page-id) (or (contains? (meta changes) ::page-id)
(contains? (meta changes) ::component-id)))) (contains? (meta changes) ::component-id))))
(defn- assert-page!
[changes]
(dm/assert!
"Call (with-page) before using this function"
(contains? (meta changes) ::page)))
(defn- assert-objects! (defn- assert-objects!
[changes] [changes]
(dm/assert! (dm/assert!
@ -195,11 +189,30 @@
(apply-changes-local))) (apply-changes-local)))
(defn mod-page (defn mod-page
[changes page new-name] ([changes options]
(-> changes (let [page (::page (meta changes))]
(update :redo-changes conj {:type :mod-page :id (:id page) :name new-name}) (mod-page changes page options)))
(update :undo-changes conj {:type :mod-page :id (:id page) :name (:name page)})
(apply-changes-local))) ([changes page {:keys [name background]}]
(let [change {:type :mod-page :id (:id page)}
redo (cond-> change
(some? name)
(assoc :name name)
(some? background)
(assoc :background background))
undo (cond-> change
(some? name)
(assoc :name (:name page))
(some? background)
(assoc :background (:background page)))]
(-> changes
(update :redo-changes conj redo)
(update :undo-changes conj undo)
(apply-changes-local)))))
(defn mod-plugin-data (defn mod-plugin-data
([changes namespace key value] ([changes namespace key value]
@ -246,42 +259,77 @@
(update :undo-changes conj {:type :mov-page :id page-id :index prev-index}) (update :undo-changes conj {:type :mov-page :id page-id :index prev-index})
(apply-changes-local))) (apply-changes-local)))
(defn set-page-option
[changes option-key option-val] (defn set-guide
(assert-page! changes) [changes id guide]
(let [page-id (::page-id (meta changes)) (let [page-id (::page-id (meta changes))
page (::page (meta changes)) page (::page (meta changes))
old-val (get-in page [:options option-key])] old-val (dm/get-in page [:guides id])]
(-> changes (-> changes
(update :redo-changes conj {:type :set-option (update :redo-changes conj {:type :set-guide
:page-id page-id :page-id page-id
:option option-key :id id
:value option-val}) :params guide})
(update :undo-changes conj {:type :set-option (update :undo-changes conj {:type :set-guide
:page-id page-id :page-id page-id
:option option-key :id id
:value old-val}) :params old-val}))))
(apply-changes-local)))) (defn set-flow
[changes id flow]
(defn update-page-option
[changes option-key update-fn & args]
(assert-page! changes)
(let [page-id (::page-id (meta changes)) (let [page-id (::page-id (meta changes))
page (::page (meta changes)) page (::page (meta changes))
old-val (get-in page [:options option-key]) old-val (dm/get-in page [:flows id])
new-val (apply update-fn old-val args)]
(-> changes changes (-> changes
(update :redo-changes conj {:type :set-option (update :redo-changes conj {:type :set-flow
:page-id page-id :page-id page-id
:option option-key :id id
:value new-val}) :params flow})
(update :undo-changes conj {:type :set-option (update :undo-changes conj {:type :set-flow
:page-id page-id :page-id page-id
:option option-key :id id
:value old-val}) :params old-val}))]
(apply-changes-local)))) ;; FIXME: not sure if we need this
(apply-changes-local changes)))
(defn set-comment-thread-position
[changes {:keys [id frame-id position] :as thread}]
(let [page-id (::page-id (meta changes))
page (::page (meta changes))
old-val (dm/get-in page [:comment-thread-positions id])
changes (-> changes
(update :redo-changes conj {:type :set-comment-thread-position
:comment-thread-id id
:page-id page-id
:frame-id frame-id
:position position})
(update :undo-changes conj {:type :set-comment-thread-position
:page-id page-id
:comment-thread-id id
:frame-id (:frame-id old-val)
:position (:position old-val)}))]
;; FIXME: not sure if we need this
(apply-changes-local changes)))
(defn set-default-grid
[changes type params]
(let [page-id (::page-id (meta changes))
page (::page (meta changes))
old-val (dm/get-in page [:grids type])
changes (update changes :redo-changes conj {:type :set-default-grid
:page-id page-id
:grid-type type
:params params})
changes (update changes :undo-changes conj {:type :set-default-grid
:page-id page-id
:grid-type type
:params old-val})]
;; FIXME: not sure if we need this
(apply-changes-local changes)))
;; Shape tree changes ;; Shape tree changes

View file

@ -6,4 +6,4 @@
(ns app.common.files.defaults) (ns app.common.files.defaults)
(def version 51) (def version 52)

View file

@ -1017,6 +1017,30 @@
(into {} (filter #(-> % val valid-color?) colors)))] (into {} (filter #(-> % val valid-color?) colors)))]
(update data :colors update-colors))) (update data :colors update-colors)))
(defn migrate-up-52
"This migration moves page options to the page level"
[data]
(let [update-page
(fn [{:keys [options] :as page}]
(cond-> page
(and (some? (:saved-grids options))
(not (contains? page :default-grids)))
(assoc :default-grids (:saved-grids options))
(and (some? (:flows options))
(not (contains? page :flows)))
(assoc :flows (:flows options))
(and (some? (:guides options))
(not (contains? page :guides)))
(assoc :guides (:guides options))
(and (some? (:comment-threads-position options))
(not (contains? page :comment-thread-positions)))
(assoc :comment-thread-positions (:comment-threads-position options))))]
(update data :pages-index d/update-vals update-page)))
(def migrations (def migrations
"A vector of all applicable migrations" "A vector of all applicable migrations"
[{:id 2 :migrate-up migrate-up-2} [{:id 2 :migrate-up migrate-up-2}
@ -1059,4 +1083,5 @@
{:id 48 :migrate-up migrate-up-48} {:id 48 :migrate-up migrate-up-48}
{:id 49 :migrate-up migrate-up-49} {:id 49 :migrate-up migrate-up-49}
{:id 50 :migrate-up migrate-up-50} {:id 50 :migrate-up migrate-up-50}
{:id 51 :migrate-up migrate-up-51}]) {:id 51 :migrate-up migrate-up-51}
{:id 52 :migrate-up migrate-up-52}])

View file

@ -15,10 +15,10 @@
[old-page page check-attrs] [old-page page check-attrs]
(let [old-objects (get old-page :objects) (let [old-objects (get old-page :objects)
old-guides (or (get-in old-page [:options :guides]) []) old-guides (or (get old-page :guides) [])
new-objects (get page :objects) new-objects (get page :objects)
new-guides (or (get-in page [:options :guides]) []) new-guides (or (get page :guides) [])
changed-object? changed-object?
(fn [id] (fn [id]

View file

@ -1947,54 +1947,54 @@
(defn generate-duplicate-flows (defn generate-duplicate-flows
[changes shapes page ids-map] [changes shapes page ids-map]
(let [flows (-> page :options :flows) (let [flows (get page :flows)
unames (volatile! (into #{} (map :name flows))) unames (volatile! (cfh/get-used-names (vals flows)))
frames-with-flow (->> shapes has-flow? (partial ctp/get-frame-flow flows)]
(filter #(= (:type %) :frame))
(filter #(some? (ctp/get-frame-flow flows (:id %)))))] (reduce (fn [changes frame-id]
(if-not (empty? frames-with-flow) (let [name (cfh/generate-unique-name @unames "Flow 1")
(let [update-flows (fn [flows] frame-id (get ids-map frame-id)
(reduce flow-id (uuid/next)
(fn [flows frame] new-flow {:id flow-id
(let [name (cfh/generate-unique-name @unames "Flow 1") :name name
_ (vswap! unames conj name) :starting-frame frame-id}]
new-flow {:id (uuid/next)
:name name (vswap! unames conj name)
:starting-frame (get ids-map (:id frame))}] (pcb/set-flow changes flow-id new-flow)))
(ctp/add-flow flows new-flow)))
flows changes
frames-with-flow))] (->> shapes
(pcb/update-page-option changes :flows update-flows)) (filter cfh/frame-shape?)
changes))) (map :id)
(filter has-flow?)))))
(defn generate-duplicate-guides (defn generate-duplicate-guides
[changes shapes page ids-map delta] [changes shapes page ids-map delta]
(let [guides (get-in page [:options :guides]) (let [guides (get page :guides)
frames (->> shapes (filter cfh/frame-shape?)) frames (filter cfh/frame-shape? shapes)]
new-guides ;; FIXME: this can be implemented efficiently just indexing guides
(reduce ;; by frame-id instead of iterate over all guides all the time
(fn [g frame]
(let [new-id (ids-map (:id frame))
new-frame (-> frame (gsh/move delta))
new-guides (reduce (fn [changes frame]
(->> guides (let [new-id (get ids-map (:id frame))
(vals) new-frame (gsh/move frame delta)]
(filter #(= (:frame-id %) (:id frame)))
(map #(-> % (reduce-kv (fn [changes _ guide]
(assoc :id (uuid/next)) (if (= (:id frame) (:frame-id guide))
(assoc :frame-id new-id) (let [guide-id (uuid/next)
(assoc :position (if (= (:axis %) :x) position (if (= (:axis guide) :x)
(+ (:position %) (- (:x new-frame) (:x frame))) (+ (:position guide) (- (:x new-frame) (:x frame)))
(+ (:position %) (- (:y new-frame) (:y frame))))))))] (+ (:position guide) (- (:y new-frame) (:y frame))))
(cond-> g guide {:id guide-id
(not-empty new-guides) :frame-id new-id
(conj (into {} (map (juxt :id identity) new-guides)))))) :position position}]
guides (pcb/set-guide changes guide-id guide))
frames)] changes))
(-> (pcb/with-page changes page) changes
(pcb/set-page-option :guides new-guides)))) guides)))
(pcb/with-page changes page)
frames)))
(defn generate-duplicate-component-change (defn generate-duplicate-component-change
[changes objects page component-root parent-id frame-id delta libraries library-data] [changes objects page component-root parent-id frame-id delta libraries library-data]

View file

@ -7,13 +7,11 @@
(ns app.common.logic.shapes (ns app.common.logic.shapes
(:require (:require
[app.common.data :as d] [app.common.data :as d]
[app.common.data.macros :as dm]
[app.common.files.changes-builder :as pcb] [app.common.files.changes-builder :as pcb]
[app.common.files.helpers :as cfh] [app.common.files.helpers :as cfh]
[app.common.geom.shapes :as gsh] [app.common.geom.shapes :as gsh]
[app.common.types.component :as ctk] [app.common.types.component :as ctk]
[app.common.types.container :as ctn] [app.common.types.container :as ctn]
[app.common.types.page :as ctp]
[app.common.types.shape.interactions :as ctsi] [app.common.types.shape.interactions :as ctsi]
[app.common.types.shape.layout :as ctl] [app.common.types.shape.layout :as ctl]
[app.common.uuid :as uuid])) [app.common.uuid :as uuid]))
@ -85,7 +83,9 @@
(pcb/with-page page) (pcb/with-page page)
(pcb/with-objects objects) (pcb/with-objects objects)
(pcb/with-library-data file)) (pcb/with-library-data file))
lookup (d/getf objects) lookup (d/getf objects)
groups-to-unmask groups-to-unmask
(reduce (fn [group-ids id] (reduce (fn [group-ids id]
;; When the shape to delete is the mask of a masked group, ;; When the shape to delete is the mask of a masked group,
@ -110,23 +110,14 @@
interactions))) interactions)))
(vals objects)) (vals objects))
ids-set (set ids-to-delete) changes
guides-to-remove (reduce (fn [changes {:keys [id] :as flow}]
(->> (dm/get-in page [:options :guides]) (if (contains? ids-to-delete (:starting-frame flow))
(vals) (pcb/set-flow changes id nil)
(filter #(contains? ids-set (:frame-id %))) changes))
(map :id)) changes
(:flows page))
guides
(->> guides-to-remove
(reduce dissoc (dm/get-in page [:options :guides])))
starting-flows
(filter (fn [flow]
;; If any of the deleted is a frame that starts a flow,
;; this must be deleted, too.
(contains? ids-to-delete (:starting-frame flow)))
(-> page :options :flows))
all-parents all-parents
(reduce (fn [res id] (reduce (fn [res id]
@ -172,8 +163,18 @@
(into ids-to-delete all-children)) (into ids-to-delete all-children))
[]) [])
changes (-> changes ids-set (set ids-to-delete)
(pcb/set-page-option :guides guides))
guides-to-delete
(->> (:guides page)
(vals)
(filter #(contains? ids-set (:frame-id %)))
(map :id))
changes (reduce (fn [changes guide-id]
(pcb/set-flow changes guide-id nil))
changes
guides-to-delete)
changes (reduce (fn [changes component-id] changes (reduce (fn [changes component-id]
;; It's important to delete the component before the main instance, because we ;; It's important to delete the component before the main instance, because we
@ -181,6 +182,7 @@
(pcb/delete-component changes component-id (:id page))) (pcb/delete-component changes component-id (:id page)))
changes changes
components-to-delete) components-to-delete)
changes (-> changes changes (-> changes
(generate-update-shape-flags ids-to-hide objects {:hidden true}) (generate-update-shape-flags ids-to-hide objects {:hidden true})
(pcb/remove-objects all-children {:ignore-touched true}) (pcb/remove-objects all-children {:ignore-touched true})
@ -197,11 +199,7 @@
(into [] (into []
(remove #(and (ctsi/has-destination %) (remove #(and (ctsi/has-destination %)
(contains? ids-to-delete (:destination %)))) (contains? ids-to-delete (:destination %))))
interactions))))) interactions))))))]
(cond-> (seq starting-flows)
(pcb/update-page-option :flows (fn [flows]
(->> (map :id starting-flows)
(reduce ctp/remove-flow flows))))))]
[all-parents changes])) [all-parents changes]))

View file

@ -6,6 +6,7 @@
(ns app.common.types.grid (ns app.common.types.grid
(:require (:require
[app.common.colors :as clr]
[app.common.schema :as sm] [app.common.schema :as sm]
[app.common.types.color :as ctc])) [app.common.types.color :as ctc]))
@ -54,7 +55,7 @@
[:display :boolean] [:display :boolean]
[:params schema:square-params]]]]) [:params schema:square-params]]]])
(def schema:saved-grids (def schema:default-grids
[:map {:title "PageGrid"} [:map {:title "PageGrid"}
[:square {:optional true} ::square-params] [:square {:optional true} ::square-params]
[:row {:optional true} ::column-params] [:row {:optional true} ::column-params]
@ -63,4 +64,24 @@
(sm/register! ::square-params schema:square-params) (sm/register! ::square-params schema:square-params)
(sm/register! ::column-params schema:column-params) (sm/register! ::column-params schema:column-params)
(sm/register! ::grid schema:grid) (sm/register! ::grid schema:grid)
(sm/register! ::saved-grids schema:saved-grids) (sm/register! ::default-grids schema:default-grids)
(def ^:private default-square-params
{:size 16
:color {:color clr/info
:opacity 0.4}})
(def ^:private default-layout-params
{:size 12
:type :stretch
:item-length nil
:gutter 8
:margin 0
:color {:color clr/default-layout
:opacity 0.1}})
(def default-grid-params
{:square default-square-params
:column default-layout-params
:row default-layout-params})

View file

@ -7,6 +7,7 @@
(ns app.common.types.page (ns app.common.types.page
(:require (:require
[app.common.data :as d] [app.common.data :as d]
[app.common.geom.point :as-alias gpt]
[app.common.schema :as sm] [app.common.schema :as sm]
[app.common.types.color :as-alias ctc] [app.common.types.color :as-alias ctc]
[app.common.types.grid :as ctg] [app.common.types.grid :as ctg]
@ -24,38 +25,56 @@
[:name :string] [:name :string]
[:starting-frame ::sm/uuid]]) [:starting-frame ::sm/uuid]])
(def schema:flows
[:map-of {:gen/max 2} ::sm/uuid schema:flow])
(def schema:guide (def schema:guide
[:map {:title "Guide"} [:map {:title "Guide"}
[:id ::sm/uuid] [:id ::sm/uuid]
[:axis [::sm/one-of #{:x :y}]] [:axis [::sm/one-of #{:x :y}]]
[:position ::sm/safe-number] [:position ::sm/safe-number]
;; FIXME: remove maybe?
[:frame-id {:optional true} [:maybe ::sm/uuid]]]) [:frame-id {:optional true} [:maybe ::sm/uuid]]])
(def schema:guides
[:map-of {:gen/max 2} ::sm/uuid schema:guide])
(def schema:objects
[:map-of {:gen/max 5} ::sm/uuid ::cts/shape])
(def schema:comment-thread-position
[:map {:title "CommentThreadPosition"}
[:frame-id ::sm/uuid]
[:position ::gpt/point]])
(def schema:page (def schema:page
[:map {:title "FilePage"} [:map {:title "FilePage"}
[:id ::sm/uuid] [:id ::sm/uuid]
[:name :string] [:name :string]
[:objects [:objects schema:objects]
[:map-of {:gen/max 5} ::sm/uuid ::cts/shape]] [:default-grids {:optional true} ::ctg/default-grids]
[:flows {:optional true} schema:flows]
[:guides {:optional true} schema:guides]
[:plugin-data {:optional true} ::ctpg/plugin-data]
[:background {:optional true} ::ctc/rgb-color]
[:comment-thread-positions {:optional true}
[:map-of ::sm/uuid schema:comment-thread-position]]
[:options [:options
[:map {:title "PageOptions"} ;; DEPERECATED: remove after 2.3 release
[:background {:optional true} ::ctc/rgb-color] [:map {:title "PageOptions"}]]])
[:saved-grids {:optional true} ::ctg/saved-grids]
[:flows {:optional true}
[:vector {:gen/max 2} schema:flow]]
[:guides {:optional true}
[:map-of {:gen/max 2} ::sm/uuid schema:guide]]
[:plugin-data {:optional true} ::ctpg/plugin-data]]]])
(sm/register! ::page schema:page) (sm/register! ::page schema:page)
(sm/register! ::guide schema:guide) (sm/register! ::guide schema:guide)
(sm/register! ::flow schema:flow) (sm/register! ::flow schema:flow)
(def check-page-guide! (def valid-guide?
(sm/check-fn ::guide)) (sm/lazy-validator schema:guide))
;; FIXME: convert to validator
(def check-page! (def check-page!
(sm/check-fn ::page)) (sm/check-fn schema:page))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; INIT & HELPERS ;; INIT & HELPERS
@ -80,25 +99,6 @@
(assoc :id (or id (uuid/next))) (assoc :id (or id (uuid/next)))
(assoc :name (or name "Page 1")))) (assoc :name (or name "Page 1"))))
;; --- Helpers for flow
(defn rename-flow
[flow name]
(assoc flow :name name))
(defn add-flow
[flows flow]
(conj (or flows []) flow))
(defn remove-flow
[flows flow-id]
(d/removev #(= (:id %) flow-id) flows))
(defn update-flow
[flows flow-id update-fn]
(let [index (d/index-of-pred flows #(= (:id %) flow-id))]
(update flows index update-fn)))
(defn get-frame-flow (defn get-frame-flow
[flows frame-id] [flows frame-id]
(d/seek #(= (:starting-frame %) frame-id) flows)) (d/seek #(= (:starting-frame %) frame-id) (vals flows)))

View file

@ -19,71 +19,6 @@
(binding [ffeat/*current* #{"components/v2"}] (binding [ffeat/*current* #{"components/v2"}]
(ctf/make-file-data file-id page-id))) (ctf/make-file-data file-id page-id)))
(t/deftest process-change-set-option
(let [file-id (uuid/custom 2 2)
page-id (uuid/custom 1 1)
data (make-file-data file-id page-id)]
(t/testing "Sets option single"
(let [chg {:type :set-option
:page-id page-id
:option :test
:value "test"}
res (ch/process-changes data [chg])]
(t/is (= "test" (get-in res [:pages-index page-id :options :test])))))
(t/testing "Sets option nested"
(let [chgs [{:type :set-option
:page-id page-id
:option [:values :test :a]
:value "a"}
{:type :set-option
:page-id page-id
:option [:values :test :b]
:value "b"}]
res (ch/process-changes data chgs)]
(t/is (= {:a "a" :b "b"}
(get-in res [:pages-index page-id :options :values :test])))))
(t/testing "Remove option single"
(let [chg {:type :set-option
:page-id page-id
:option :test
:value nil}
res (ch/process-changes data [chg])]
(t/is (empty? (keys (get-in res [:pages-index page-id :options]))))))
(t/testing "Remove option nested 1"
(let [chgs [{:type :set-option
:page-id page-id
:option [:values :test :a]
:value "a"}
{:type :set-option
:page-id page-id
:option [:values :test :b]
:value "b"}
{:type :set-option
:page-id page-id
:option [:values :test]
:value nil}]
res (ch/process-changes data chgs)]
(t/is (empty? (keys (get-in res [:pages-index page-id :options]))))))
(t/testing "Remove option nested 2"
(let [chgs [{:type :set-option
:option [:values :test1 :a]
:page-id page-id
:value "a"}
{:type :set-option
:option [:values :test2 :b]
:page-id page-id
:value "b"}
{:type :set-option
:page-id page-id
:option [:values :test2]
:value nil}]
res (ch/process-changes data chgs)]
(t/is (= [:test1] (keys (get-in res [:pages-index page-id :options :values]))))))))
(t/deftest process-change-add-obj (t/deftest process-change-add-obj
(let [file-id (uuid/custom 2 2) (let [file-id (uuid/custom 2 2)
page-id (uuid/custom 1 1) page-id (uuid/custom 1 1)

View file

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

View file

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

View file

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

View file

@ -6,10 +6,10 @@
(ns app.main.data.workspace.grid (ns app.main.data.workspace.grid
(:require (:require
[app.common.colors :as clr]
[app.common.data :as d] [app.common.data :as d]
[app.common.data.macros :as dm] [app.common.data.macros :as dm]
[app.common.files.changes-builder :as pcb] [app.common.files.changes-builder :as pcb]
[app.common.types.grid :as ctg]
[app.main.data.changes :as dch] [app.main.data.changes :as dch]
[app.main.data.workspace.shapes :as dwsh] [app.main.data.workspace.shapes :as dwsh]
[app.main.data.workspace.state-helpers :as wsh] [app.main.data.workspace.state-helpers :as wsh]
@ -20,25 +20,6 @@
;; Grid ;; 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 (defn add-frame-grid
[frame-id] [frame-id]
(dm/assert! (uuid? frame-id)) (dm/assert! (uuid? frame-id))
@ -46,9 +27,9 @@
ptk/WatchEvent ptk/WatchEvent
(watch [_ state _] (watch [_ state _]
(let [page-id (:current-page-id state) (let [page-id (:current-page-id state)
data (get-in state [:workspace-data :pages-index page-id]) page (dm/get-in state [:workspace-data :pages-index page-id])
params (or (get-in data [:options :saved-grids :square]) params (or (dm/get-in page [:grids :square])
(:square default-grid-params)) (:square ctg/default-grid-params))
grid {:type :square grid {:type :square
:params params :params params
:display true}] :display true}]
@ -79,4 +60,4 @@
(rx/of (dch/commit-changes (rx/of (dch/commit-changes
(-> (pcb/empty-changes it) (-> (pcb/empty-changes it)
(pcb/with-page page) (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] [beicon.v2.core :as rx]
[potok.v2.core :as ptk])) [potok.v2.core :as ptk]))
(defn make-update-guide
[guide]
(fn [other]
(cond-> other
(= (:id other) (:id guide))
(merge guide))))
(defn update-guides (defn update-guides
[guide] [{:keys [id] :as guide}]
(dm/assert! (dm/assert!
"expected valid guide" "expected valid guide"
(ctp/check-page-guide! guide)) (ctp/valid-guide? guide))
(ptk/reify ::update-guides (ptk/reify ::update-guides
ev/Event ev/Event
@ -41,14 +35,15 @@
changes changes
(-> (pcb/empty-changes it) (-> (pcb/empty-changes it)
(pcb/with-page page) (pcb/with-page page)
(pcb/update-page-option :guides assoc (:id guide) guide))] (pcb/set-guide id guide))]
(rx/of (dwc/commit-changes changes)))))) (rx/of (dwc/commit-changes changes))))))
(defn remove-guide (defn remove-guide
[guide] [{:keys [id] :as guide}]
(dm/assert! (dm/assert!
"expected valid guide" "expected valid guide"
(ctp/check-page-guide! guide)) (ctp/valid-guide? guide))
(ptk/reify ::remove-guide (ptk/reify ::remove-guide
ev/Event ev/Event
@ -57,7 +52,7 @@
ptk/UpdateEvent ptk/UpdateEvent
(update [_ state] (update [_ state]
(let [sdisj (fnil disj #{})] (let [sdisj (fnil disj #{})]
(update-in state [:workspace-guides :hover] sdisj (:id guide)))) (update-in state [:workspace-guides :hover] sdisj id)))
ptk/WatchEvent ptk/WatchEvent
(watch [it state _] (watch [it state _]
@ -65,18 +60,22 @@
changes changes
(-> (pcb/empty-changes it) (-> (pcb/empty-changes it)
(pcb/with-page page) (pcb/with-page page)
(pcb/update-page-option :guides dissoc (:id guide)))] (pcb/set-guide id nil))]
(rx/of (dwc/commit-changes changes)))))) (rx/of (dwc/commit-changes changes))))))
(defn remove-guides (defn remove-guides
[ids] [ids]
(dm/assert!
"expected a set of ids"
(every? uuid? ids))
(ptk/reify ::remove-guides (ptk/reify ::remove-guides
ptk/WatchEvent ptk/WatchEvent
(watch [_ state _] (watch [_ state _]
(let [page (wsh/lookup-page state) (let [{:keys [guides] :as page} (wsh/lookup-page state)
guides (get-in page [:options :guides] {})
guides (-> (select-keys guides ids) (vals))] guides (-> (select-keys guides ids) (vals))]
(rx/from (->> guides (mapv #(remove-guide %)))))))) (rx/from (mapv remove-guide guides))))))
(defmethod ptk/resolve ::move-frame-guides (defmethod ptk/resolve ::move-frame-guides
[_ args] [_ args]
@ -105,7 +104,7 @@
guide (update guide :position + (get moved (:axis guide)))] guide (update guide :position + (get moved (:axis guide)))]
(update-guides guide))) (update-guides guide)))
guides (-> state wsh/lookup-page-options :guides vals)] guides (-> state wsh/lookup-page :guides vals)]
(->> guides (->> guides
(filter (comp frame-ids? :frame-id)) (filter (comp frame-ids? :frame-id))

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -139,7 +139,7 @@
:viewport-size) :viewport-size)
tpos-ref (mf/with-memo [page-id] 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))) (l/derived refs/viewer-data)))
positions (mf/deref tpos-ref) positions (mf/deref tpos-ref)

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -41,7 +41,8 @@
positions-ref positions-ref
(mf/with-memo [page-id] (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))) (l/derived st/state)))
positions (mf/deref positions-ref) positions (mf/deref positions-ref)

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -46,7 +46,6 @@
page' (cthf/current-page file') page' (cthf/current-page file')
guide' (-> page' guide' (-> page'
:options
:guides :guides
(vals) (vals)
(first))] (first))]

View file

@ -391,9 +391,9 @@
(t/testing "Update global guide" (t/testing "Update global guide"
(let [guide {:position 50 :axis :x} (let [guide {:position 50 :axis :x}
file (-> (fb/create-file "Test") file (-> (fb/create-file "Test")
(fb/add-page {:name "Page 1"}) (fb/add-page {:name "Page 1"})
(fb/add-guide guide)) (fb/add-guide guide))
guide-id (:last-id file) guide-id (:last-id file)
guide (assoc guide :id guide-id) guide (assoc guide :id guide-id)
@ -403,7 +403,7 @@
frame-id (:last-id file) frame-id (:last-id file)
page (fb/get-current-page file) page (fb/get-current-page file)
data (-> (sd/make-snap-data) (sd/add-page page)) data (-> (sd/make-snap-data) (sd/add-page page))
new-page (-> (fb/update-guide file (assoc guide :position 150)) new-page (-> (fb/update-guide file (assoc guide :position 150))
(fb/get-current-page)) (fb/get-current-page))