mirror of
https://github.com/penpot/penpot.git
synced 2025-04-11 06:21:30 -05:00
✨ Rework nested components to avoid indirect references
This commit is contained in:
parent
14d10af9b8
commit
c38d0e3211
5 changed files with 674 additions and 517 deletions
|
@ -35,13 +35,34 @@
|
|||
(defn get-root-shape
|
||||
"Get the root shape linked to a component for this shape, if any"
|
||||
[shape objects]
|
||||
(if (:component-root? shape)
|
||||
(if (:component-id shape)
|
||||
shape
|
||||
(if-let [parent-id (:parent-id shape)]
|
||||
(get-root-shape (get objects (:parent-id shape))
|
||||
objects)
|
||||
nil)))
|
||||
|
||||
(defn get-container
|
||||
[page-id component-id local-file]
|
||||
(if (some? page-id)
|
||||
(get-in local-file [:pages-index page-id])
|
||||
(get-in local-file [:components component-id])))
|
||||
|
||||
(defn get-shape
|
||||
[container shape-id]
|
||||
(get-in container [:objects shape-id]))
|
||||
|
||||
(defn get-component
|
||||
[component-id file-id local-file libraries]
|
||||
(let [file (if (nil? file-id)
|
||||
local-file
|
||||
(get-in libraries [file-id :data]))]
|
||||
(get-in file [:components component-id])))
|
||||
|
||||
(defn get-component-root
|
||||
[component]
|
||||
(get-in component [:objects (:id component)]))
|
||||
|
||||
(defn get-children
|
||||
"Retrieve all children ids recursively for a given object"
|
||||
[id objects]
|
||||
|
|
|
@ -20,18 +20,20 @@
|
|||
(defn show
|
||||
([props]
|
||||
(show (uuid/next) (:type props) props))
|
||||
([type props] (show (uuid/next) type props))
|
||||
([type props]
|
||||
(show (uuid/next) type props))
|
||||
([id type props]
|
||||
(ptk/reify ::show-modal
|
||||
ptk/UpdateEvent
|
||||
(update [_ state]
|
||||
(assoc state ::modal {:id id
|
||||
:type type
|
||||
:props props
|
||||
:allow-click-outside false})))))
|
||||
:type type
|
||||
:props props
|
||||
:allow-click-outside false})))))
|
||||
|
||||
(defn update-props
|
||||
([type props]
|
||||
(ptk/reify ::show-modal
|
||||
(ptk/reify ::update-modal-props
|
||||
ptk/UpdateEvent
|
||||
(update [_ state]
|
||||
(cond-> state
|
||||
|
|
|
@ -125,423 +125,6 @@
|
|||
:object prev}]
|
||||
(rx/of (dwc/commit-changes [rchg] [uchg] {:commit-local? true}))))))
|
||||
|
||||
(def add-component
|
||||
(ptk/reify ::add-component
|
||||
ptk/WatchEvent
|
||||
(watch [_ state stream]
|
||||
(let [page-id (:current-page-id state)
|
||||
objects (dwc/lookup-page-objects state page-id)
|
||||
selected (get-in state [:workspace-local :selected])
|
||||
shapes (dws/shapes-for-grouping objects selected)]
|
||||
(when-not (empty? shapes)
|
||||
(let [;; If the selected shape is a group, we can use it. If not,
|
||||
;; we need to create a group before creating the component.
|
||||
[group rchanges uchanges]
|
||||
(if (and (= (count shapes) 1)
|
||||
(= (:type (first shapes)) :group))
|
||||
[(first shapes) [] []]
|
||||
(dws/prepare-create-group page-id shapes "Component-" true))
|
||||
|
||||
[new-shape new-shapes updated-shapes]
|
||||
(dwlh/make-component-shape group objects)
|
||||
|
||||
rchanges (conj rchanges
|
||||
{:type :add-component
|
||||
:id (:id new-shape)
|
||||
:name (:name new-shape)
|
||||
:shapes new-shapes})
|
||||
|
||||
rchanges (into rchanges
|
||||
(map (fn [updated-shape]
|
||||
{:type :mod-obj
|
||||
:page-id page-id
|
||||
:id (:id updated-shape)
|
||||
:operations [{:type :set
|
||||
:attr :component-id
|
||||
:val (:component-id updated-shape)}
|
||||
{:type :set
|
||||
:attr :component-file
|
||||
:val nil}
|
||||
{:type :set
|
||||
:attr :component-root?
|
||||
:val (:component-root? updated-shape)}
|
||||
{:type :set
|
||||
:attr :shape-ref
|
||||
:val (:shape-ref updated-shape)}
|
||||
{:type :set
|
||||
:attr :touched
|
||||
:val (:touched updated-shape)}]})
|
||||
updated-shapes))
|
||||
|
||||
uchanges (conj uchanges
|
||||
{:type :del-component
|
||||
:id (:id new-shape)})
|
||||
|
||||
uchanges (into uchanges
|
||||
(map (fn [updated-shape]
|
||||
(let [original-shape (get objects (:id updated-shape))]
|
||||
{:type :mod-obj
|
||||
:page-id page-id
|
||||
:id (:id updated-shape)
|
||||
:operations [{:type :set
|
||||
:attr :component-id
|
||||
:val (:component-id original-shape)}
|
||||
{:type :set
|
||||
:attr :component-file
|
||||
:val (:component-file original-shape)}
|
||||
{:type :set
|
||||
:attr :component-root?
|
||||
:val (:component-root? original-shape)}
|
||||
{:type :set
|
||||
:attr :shape-ref
|
||||
:val (:shape-ref original-shape)}
|
||||
{:type :set
|
||||
:attr :touched
|
||||
:val (:touched original-shape)}]}))
|
||||
updated-shapes))]
|
||||
|
||||
|
||||
(rx/of (dwc/commit-changes rchanges uchanges {:commit-local? true})
|
||||
(dws/select-shapes (d/ordered-set (:id group))))))))))
|
||||
|
||||
(defn delete-component
|
||||
[{:keys [id] :as params}]
|
||||
(us/assert ::us/uuid id)
|
||||
(ptk/reify ::delete-component
|
||||
ptk/WatchEvent
|
||||
(watch [_ state stream]
|
||||
(let [component (get-in state [:workspace-data :components id])
|
||||
|
||||
rchanges [{:type :del-component
|
||||
:id id}]
|
||||
|
||||
uchanges [{:type :add-component
|
||||
:id id
|
||||
:name (:name component)
|
||||
:shapes (vals (:objects component))}]]
|
||||
|
||||
(rx/of (dwc/commit-changes rchanges uchanges {:commit-local? true}))))))
|
||||
|
||||
(defn instantiate-component
|
||||
[file-id component-id position]
|
||||
(us/assert (s/nilable ::us/uuid) file-id)
|
||||
(us/assert ::us/uuid component-id)
|
||||
(ptk/reify ::instantiate-component
|
||||
ptk/WatchEvent
|
||||
(watch [_ state stream]
|
||||
(let [component (if (nil? file-id)
|
||||
(get-in state [:workspace-data :components component-id])
|
||||
(get-in state [:workspace-libraries file-id :data :components component-id]))
|
||||
component-shape (get-in component [:objects (:id component)])
|
||||
|
||||
orig-pos (gpt/point (:x component-shape) (:y component-shape))
|
||||
delta (gpt/subtract position orig-pos)
|
||||
|
||||
page-id (:current-page-id state)
|
||||
objects (dwc/lookup-page-objects state page-id)
|
||||
unames (atom (dwc/retrieve-used-names objects))
|
||||
|
||||
frame-id (cph/frame-id-by-position objects (gpt/add orig-pos delta))
|
||||
|
||||
update-new-shape
|
||||
(fn [new-shape original-shape]
|
||||
(let [new-name
|
||||
(dwc/generate-unique-name @unames (:name new-shape))]
|
||||
|
||||
(swap! unames conj new-name)
|
||||
|
||||
(cond-> new-shape
|
||||
true
|
||||
(as-> $
|
||||
(assoc $ :name new-name)
|
||||
(geom/move $ delta)
|
||||
(assoc $ :frame-id frame-id)
|
||||
(assoc $ :parent-id
|
||||
(or (:parent-id $) (:frame-id $)))
|
||||
(assoc $ :shape-ref (:id original-shape))
|
||||
(dissoc $ :touched))
|
||||
|
||||
(nil? (:parent-id original-shape))
|
||||
(assoc :component-id (:id original-shape)
|
||||
:component-root? true)
|
||||
|
||||
(and (nil? (:parent-id original-shape)) (some? file-id))
|
||||
(assoc :component-file file-id)
|
||||
|
||||
(and (nil? (:parent-id original-shape)) (nil? file-id))
|
||||
(dissoc :component-file)
|
||||
|
||||
(some? (:parent-id original-shape))
|
||||
(dissoc :component-root?))))
|
||||
|
||||
[new-shape new-shapes _]
|
||||
(cph/clone-object component-shape
|
||||
nil
|
||||
(get component :objects)
|
||||
update-new-shape)
|
||||
|
||||
rchanges (map (fn [obj]
|
||||
{:type :add-obj
|
||||
:id (:id obj)
|
||||
:page-id page-id
|
||||
:frame-id (:frame-id obj)
|
||||
:parent-id (:parent-id obj)
|
||||
:obj obj})
|
||||
new-shapes)
|
||||
|
||||
uchanges (map (fn [obj]
|
||||
{:type :del-obj
|
||||
:id (:id obj)
|
||||
:page-id page-id})
|
||||
new-shapes)]
|
||||
|
||||
(rx/of (dwc/commit-changes rchanges uchanges {:commit-local? true})
|
||||
(dws/select-shapes (d/ordered-set (:id new-shape))))))))
|
||||
|
||||
(defn detach-component
|
||||
[id]
|
||||
(us/assert ::us/uuid id)
|
||||
(ptk/reify ::detach-component
|
||||
ptk/WatchEvent
|
||||
(watch [_ state stream]
|
||||
(let [page-id (:current-page-id state)
|
||||
objects (dwc/lookup-page-objects state page-id)
|
||||
shapes (cph/get-object-with-children id objects)
|
||||
|
||||
rchanges (map (fn [obj]
|
||||
{:type :mod-obj
|
||||
:page-id page-id
|
||||
:id (:id obj)
|
||||
:operations [{:type :set
|
||||
:attr :component-id
|
||||
:val nil}
|
||||
{:type :set
|
||||
:attr :component-file
|
||||
:val nil}
|
||||
{:type :set
|
||||
:attr :shape-ref
|
||||
:val nil}]})
|
||||
shapes)
|
||||
|
||||
uchanges (map (fn [obj]
|
||||
{:type :mod-obj
|
||||
:page-id page-id
|
||||
:id (:id obj)
|
||||
:operations [{:type :set
|
||||
:attr :component-id
|
||||
:val (:component-id obj)}
|
||||
{:type :set
|
||||
:attr :component-file
|
||||
:val (:component-file obj)}
|
||||
{:type :set
|
||||
:attr :shape-ref
|
||||
:val (:shape-ref obj)}]})
|
||||
shapes)]
|
||||
|
||||
(rx/of (dwc/commit-changes rchanges uchanges {:commit-local? true}))))))
|
||||
|
||||
(defn nav-to-component-file
|
||||
[file-id]
|
||||
(us/assert ::us/uuid file-id)
|
||||
(ptk/reify ::nav-to-component-file
|
||||
ptk/WatchEvent
|
||||
(watch [_ state stream]
|
||||
(let [file (get-in state [:workspace-libraries file-id])
|
||||
pparams {:project-id (:project-id file)
|
||||
:file-id (:id file)}
|
||||
qparams {:page-id (first (get-in file [:data :pages]))}]
|
||||
(st/emit! (rt/nav-new-window :workspace pparams qparams))))))
|
||||
|
||||
(defn ext-library-changed
|
||||
[file-id modified-at changes]
|
||||
(us/assert ::us/uuid file-id)
|
||||
(us/assert ::cp/changes changes)
|
||||
(ptk/reify ::ext-library-changed
|
||||
ptk/UpdateEvent
|
||||
(update [_ state]
|
||||
(-> state
|
||||
(assoc-in [:workspace-libraries file-id :modified-at] modified-at)
|
||||
(d/update-in-when [:workspace-libraries file-id :data]
|
||||
cp/process-changes changes)))))
|
||||
|
||||
(defn reset-component
|
||||
[id]
|
||||
(us/assert ::us/uuid id)
|
||||
(ptk/reify ::reset-component
|
||||
ptk/WatchEvent
|
||||
(watch [_ state stream]
|
||||
;; ===== Uncomment this to debug =====
|
||||
;; (js/console.info "##### RESET-COMPONENT of shape" (str id))
|
||||
(let [page-id (:current-page-id state)
|
||||
page (get-in state [:workspace-data :pages-index page-id])
|
||||
objects (dwc/lookup-page-objects state page-id)
|
||||
shape (get objects id)
|
||||
file-id (get shape :component-file)
|
||||
|
||||
[all-shapes component root-component]
|
||||
(dwlh/resolve-shapes-and-components shape
|
||||
objects
|
||||
state
|
||||
true)
|
||||
|
||||
;; ===== Uncomment this to debug =====
|
||||
;; _ (js/console.info "shape" (:name shape) "<- component" (:name component))
|
||||
;; _ (js/console.debug "all-shapes" (clj->js all-shapes))
|
||||
;; _ (js/console.debug "component" (clj->js component))
|
||||
;; _ (js/console.debug "root-component" (clj->js root-component))
|
||||
|
||||
[rchanges uchanges]
|
||||
(dwlh/generate-sync-shape-and-children-components shape
|
||||
all-shapes
|
||||
component
|
||||
root-component
|
||||
(:id page)
|
||||
nil
|
||||
true)]
|
||||
|
||||
;; ===== Uncomment this to debug =====
|
||||
;; (js/console.debug "rchanges" (clj->js rchanges))
|
||||
|
||||
(rx/of (dwc/commit-changes rchanges uchanges {:commit-local? true}))))))
|
||||
|
||||
(defn update-component
|
||||
[id]
|
||||
(us/assert ::us/uuid id)
|
||||
(ptk/reify ::update-component
|
||||
ptk/WatchEvent
|
||||
(watch [_ state stream]
|
||||
;; ===== Uncomment this to debug =====
|
||||
;; (js/console.info "##### UPDATE-COMPONENT of shape" (str id))
|
||||
(let [page-id (:current-page-id state)
|
||||
objects (dwc/lookup-page-objects state page-id)
|
||||
shape (get objects id)
|
||||
file-id (get shape :component-file)
|
||||
|
||||
[all-shapes component root-component]
|
||||
(dwlh/resolve-shapes-and-components shape
|
||||
objects
|
||||
state
|
||||
true)
|
||||
|
||||
;; ===== Uncomment this to debug =====
|
||||
;; _ (js/console.info "shape" (:name shape) "-> component" (:name component))
|
||||
;; _ (js/console.debug "all-shapes" (clj->js all-shapes))
|
||||
;; _ (js/console.debug "component" (clj->js component))
|
||||
;; _ (js/console.debug "root-component" (clj->js root-component))
|
||||
|
||||
[rchanges uchanges]
|
||||
(dwlh/generate-sync-shape-inverse shape
|
||||
all-shapes
|
||||
component
|
||||
root-component
|
||||
page-id)]
|
||||
|
||||
;; ===== Uncomment this to debug =====
|
||||
;; (js/console.debug "rchanges" (clj->js rchanges))
|
||||
|
||||
(rx/of (dwc/commit-changes rchanges uchanges {:commit-local? true}))))))
|
||||
|
||||
(declare sync-file-2nd-stage)
|
||||
|
||||
(defn sync-file
|
||||
[file-id]
|
||||
(us/assert (s/nilable ::us/uuid) file-id)
|
||||
(ptk/reify ::sync-file
|
||||
ptk/UpdateEvent
|
||||
(update [_ state]
|
||||
(if file-id
|
||||
(assoc-in state [:workspace-libraries file-id :synced-at] (dt/now))
|
||||
state))
|
||||
|
||||
ptk/WatchEvent
|
||||
(watch [_ state stream]
|
||||
;; ===== Uncomment this to debug =====
|
||||
;; (js/console.info "##### SYNC-FILE" (str (or file-id "local")))
|
||||
(let [library-changes [(dwlh/generate-sync-library :components file-id state)
|
||||
(dwlh/generate-sync-library :colors file-id state)
|
||||
(dwlh/generate-sync-library :typographies file-id state)]
|
||||
file-changes [(dwlh/generate-sync-file :components file-id state)
|
||||
(dwlh/generate-sync-file :colors file-id state)
|
||||
(dwlh/generate-sync-file :typographies file-id state)]
|
||||
rchanges (d/concat []
|
||||
(->> library-changes (remove nil?) (map first) (flatten))
|
||||
(->> file-changes (remove nil?) (map first) (flatten)))
|
||||
uchanges (d/concat []
|
||||
(->> library-changes (remove nil?) (map second) (flatten))
|
||||
(->> file-changes (remove nil?) (map second) (flatten)))]
|
||||
;; ===== Uncomment this to debug =====
|
||||
;; (js/console.debug "rchanges" (clj->js rchanges))
|
||||
(rx/concat
|
||||
(rx/of (dm/hide-tag :sync-dialog))
|
||||
(when rchanges
|
||||
(rx/of (dwc/commit-changes rchanges uchanges {:commit-local? true})))
|
||||
(when file-id
|
||||
(rp/mutation :update-sync
|
||||
{:file-id (get-in state [:workspace-file :id])
|
||||
:library-id file-id}))
|
||||
(when (some? library-changes)
|
||||
(rx/of (sync-file-2nd-stage file-id))))))))
|
||||
|
||||
(defn sync-file-2nd-stage
|
||||
"If some components have been modified, we need to launch another synchronization
|
||||
to update the instances of the changed components."
|
||||
;; TODO: this does not work if there are multiple nested components. Only the
|
||||
;; first level will be updated.
|
||||
;; To solve this properly, it would be better to launch another sync-file
|
||||
;; recursively. But for this not to cause an infinite loop, we need to
|
||||
;; implement updated-at at component level, to detect what components have
|
||||
;; not changed, and then not to apply sync and terminate the loop.
|
||||
[file-id]
|
||||
(us/assert (s/nilable ::us/uuid) file-id)
|
||||
(ptk/reify ::sync-file-2nd-stage
|
||||
ptk/WatchEvent
|
||||
(watch [_ state stream]
|
||||
;; ===== Uncomment this to debug =====
|
||||
;; (js/console.info "##### SYNC-FILE" (str (or file-id "local")) "(2nd stage)")
|
||||
(let [[rchanges1 uchanges1] (dwlh/generate-sync-file :components nil state)
|
||||
[rchanges2 uchanges2] (dwlh/generate-sync-library :components file-id state)
|
||||
rchanges (d/concat rchanges1 rchanges2)
|
||||
uchanges (d/concat uchanges1 uchanges2)]
|
||||
(when rchanges
|
||||
;; ===== Uncomment this to debug =====
|
||||
;; (js/console.debug "rchanges" (clj->js rchanges))
|
||||
(rx/of (dwc/commit-changes rchanges uchanges {:commit-local? true})))))))
|
||||
|
||||
(def ignore-sync
|
||||
(ptk/reify ::sync-file
|
||||
ptk/UpdateEvent
|
||||
(update [_ state]
|
||||
(assoc-in state [:workspace-file :ignore-sync-until] (dt/now)))
|
||||
|
||||
ptk/WatchEvent
|
||||
(watch [_ state stream]
|
||||
(rp/mutation :ignore-sync
|
||||
{:file-id (get-in state [:workspace-file :id])
|
||||
:date (dt/now)}))))
|
||||
|
||||
(defn notify-sync-file
|
||||
[file-id]
|
||||
(us/assert ::us/uuid file-id)
|
||||
(ptk/reify ::notify-sync-file
|
||||
ptk/WatchEvent
|
||||
(watch [_ state stream]
|
||||
(let [libraries-need-sync (filter #(> (:modified-at %) (:synced-at %))
|
||||
(vals (get state :workspace-libraries)))
|
||||
do-update #(do (apply st/emit! (map (fn [library]
|
||||
(sync-file (:id library)))
|
||||
libraries-need-sync))
|
||||
(st/emit! dm/hide))
|
||||
do-dismiss #(do (st/emit! ignore-sync)
|
||||
(st/emit! dm/hide))]
|
||||
(rx/of (dm/info-dialog
|
||||
(tr "workspace.updates.there-are-updates")
|
||||
:inline-actions
|
||||
[{:label (tr "workspace.updates.update")
|
||||
:callback do-update}
|
||||
{:label (tr "workspace.updates.dismiss")
|
||||
:callback do-dismiss}]
|
||||
:sync-dialog))))))
|
||||
|
||||
(defn add-typography
|
||||
([typography] (add-typography typography true))
|
||||
([typography edit?]
|
||||
|
@ -586,3 +169,417 @@
|
|||
uchg {:type :add-typography
|
||||
:typography prev}]
|
||||
(rx/of (dwc/commit-changes [rchg] [uchg] {:commit-local? true}))))))
|
||||
|
||||
(def add-component
|
||||
"Add a new component to current file library, from the currently selected shapes"
|
||||
(ptk/reify ::add-component
|
||||
ptk/WatchEvent
|
||||
(watch [_ state stream]
|
||||
(let [page-id (:current-page-id state)
|
||||
objects (dwc/lookup-page-objects state page-id)
|
||||
selected (get-in state [:workspace-local :selected])
|
||||
shapes (dws/shapes-for-grouping objects selected)]
|
||||
(when-not (empty? shapes)
|
||||
(let [;; If the selected shape is a group, we can use it. If not,
|
||||
;; we need to create a group before creating the component.
|
||||
[group rchanges uchanges]
|
||||
(if (and (= (count shapes) 1)
|
||||
(= (:type (first shapes)) :group))
|
||||
[(first shapes) [] []]
|
||||
(dws/prepare-create-group page-id shapes "Component-" true))
|
||||
|
||||
[new-shape new-shapes updated-shapes]
|
||||
(dwlh/make-component-shape group objects)
|
||||
|
||||
rchanges (conj rchanges
|
||||
{:type :add-component
|
||||
:id (:id new-shape)
|
||||
:name (:name new-shape)
|
||||
:shapes new-shapes})
|
||||
|
||||
rchanges (into rchanges
|
||||
(map (fn [updated-shape]
|
||||
{:type :mod-obj
|
||||
:page-id page-id
|
||||
:id (:id updated-shape)
|
||||
:operations [{:type :set
|
||||
:attr :component-id
|
||||
:val (:component-id updated-shape)}
|
||||
{:type :set
|
||||
:attr :component-file
|
||||
:val (:component-file updated-shape)}
|
||||
{:type :set
|
||||
:attr :component-root?
|
||||
:val (:component-root? updated-shape)}
|
||||
{:type :set
|
||||
:attr :shape-ref
|
||||
:val (:shape-ref updated-shape)}
|
||||
{:type :set
|
||||
:attr :touched
|
||||
:val (:touched updated-shape)}]})
|
||||
updated-shapes))
|
||||
|
||||
uchanges (conj uchanges
|
||||
{:type :del-component
|
||||
:id (:id new-shape)})
|
||||
|
||||
uchanges (into uchanges
|
||||
(map (fn [updated-shape]
|
||||
(let [original-shape (get objects (:id updated-shape))]
|
||||
{:type :mod-obj
|
||||
:page-id page-id
|
||||
:id (:id updated-shape)
|
||||
:operations [{:type :set
|
||||
:attr :component-id
|
||||
:val (:component-id original-shape)}
|
||||
{:type :set
|
||||
:attr :component-file
|
||||
:val (:component-file original-shape)}
|
||||
{:type :set
|
||||
:attr :component-root?
|
||||
:val (:component-root? original-shape)}
|
||||
{:type :set
|
||||
:attr :shape-ref
|
||||
:val (:shape-ref original-shape)}
|
||||
{:type :set
|
||||
:attr :touched
|
||||
:val (:touched original-shape)}]}))
|
||||
updated-shapes))]
|
||||
|
||||
|
||||
(rx/of (dwc/commit-changes rchanges uchanges {:commit-local? true})
|
||||
(dws/select-shapes (d/ordered-set (:id group))))))))))
|
||||
|
||||
(defn delete-component
|
||||
"Delete the component with the given id, from the current file library."
|
||||
[{:keys [id] :as params}]
|
||||
(us/assert ::us/uuid id)
|
||||
(ptk/reify ::delete-component
|
||||
ptk/WatchEvent
|
||||
(watch [_ state stream]
|
||||
(let [component (get-in state [:workspace-data :components id])
|
||||
|
||||
rchanges [{:type :del-component
|
||||
:id id}]
|
||||
|
||||
uchanges [{:type :add-component
|
||||
:id id
|
||||
:name (:name component)
|
||||
:shapes (vals (:objects component))}]]
|
||||
|
||||
(rx/of (dwc/commit-changes rchanges uchanges {:commit-local? true}))))))
|
||||
|
||||
(defn instantiate-component
|
||||
"Create a new shape in the current page, from the component with the given id
|
||||
in the given file library (if file-id is nil, take it from the current file library)."
|
||||
[file-id component-id position]
|
||||
(us/assert (s/nilable ::us/uuid) file-id)
|
||||
(us/assert ::us/uuid component-id)
|
||||
(us/assert ::us/point position)
|
||||
(ptk/reify ::instantiate-component
|
||||
ptk/WatchEvent
|
||||
(watch [_ state stream]
|
||||
(let [component (if (nil? file-id)
|
||||
(get-in state [:workspace-data :components component-id])
|
||||
(get-in state [:workspace-libraries file-id :data :components component-id]))
|
||||
component-shape (get-in component [:objects (:id component)])
|
||||
|
||||
orig-pos (gpt/point (:x component-shape) (:y component-shape))
|
||||
delta (gpt/subtract position orig-pos)
|
||||
|
||||
page-id (:current-page-id state)
|
||||
objects (dwc/lookup-page-objects state page-id)
|
||||
unames (atom (dwc/retrieve-used-names objects))
|
||||
|
||||
frame-id (cph/frame-id-by-position objects (gpt/add orig-pos delta))
|
||||
|
||||
update-new-shape
|
||||
(fn [new-shape original-shape]
|
||||
(let [new-name
|
||||
(dwc/generate-unique-name @unames (:name new-shape))]
|
||||
|
||||
(swap! unames conj new-name)
|
||||
|
||||
(cond-> new-shape
|
||||
true
|
||||
(as-> $
|
||||
(assoc $ :name new-name)
|
||||
(geom/move $ delta)
|
||||
(assoc $ :frame-id frame-id)
|
||||
(assoc $ :parent-id
|
||||
(or (:parent-id $) (:frame-id $))))
|
||||
|
||||
(nil? (:shape-ref original-shape))
|
||||
(assoc :shape-ref (:id original-shape))
|
||||
|
||||
(nil? (:parent-id original-shape))
|
||||
(assoc :component-id (:id original-shape)
|
||||
:component-root? true)
|
||||
|
||||
(and (nil? (:parent-id original-shape)) (some? file-id))
|
||||
(assoc :component-file file-id)
|
||||
|
||||
(and (nil? (:parent-id original-shape)) (nil? file-id))
|
||||
(dissoc :component-file)
|
||||
|
||||
(and (some? (:component-id original-shape))
|
||||
(nil? (:component-file original-shape))
|
||||
(some? file-id))
|
||||
(assoc :component-file file-id)
|
||||
|
||||
(some? (:parent-id original-shape))
|
||||
(dissoc :component-root?))))
|
||||
|
||||
[new-shape new-shapes _]
|
||||
(cph/clone-object component-shape
|
||||
nil
|
||||
(get component :objects)
|
||||
update-new-shape)
|
||||
|
||||
rchanges (map (fn [obj]
|
||||
{:type :add-obj
|
||||
:id (:id obj)
|
||||
:page-id page-id
|
||||
:frame-id (:frame-id obj)
|
||||
:parent-id (:parent-id obj)
|
||||
:obj obj})
|
||||
new-shapes)
|
||||
|
||||
uchanges (map (fn [obj]
|
||||
{:type :del-obj
|
||||
:id (:id obj)
|
||||
:page-id page-id})
|
||||
new-shapes)]
|
||||
|
||||
(rx/of (dwc/commit-changes rchanges uchanges {:commit-local? true})
|
||||
(dws/select-shapes (d/ordered-set (:id new-shape))))))))
|
||||
|
||||
(defn detach-component
|
||||
"Remove all references to components in the shape with the given id,
|
||||
and all its children, at the current page."
|
||||
[id]
|
||||
(us/assert ::us/uuid id)
|
||||
(ptk/reify ::detach-component
|
||||
ptk/WatchEvent
|
||||
(watch [_ state stream]
|
||||
(let [page-id (:current-page-id state)
|
||||
objects (dwc/lookup-page-objects state page-id)
|
||||
shapes (cph/get-object-with-children id objects)
|
||||
|
||||
rchanges (map (fn [obj]
|
||||
{:type :mod-obj
|
||||
:page-id page-id
|
||||
:id (:id obj)
|
||||
:operations [{:type :set
|
||||
:attr :component-id
|
||||
:val nil}
|
||||
{:type :set
|
||||
:attr :component-file
|
||||
:val nil}
|
||||
{:type :set
|
||||
:attr :component-root?
|
||||
:val nil}
|
||||
{:type :set
|
||||
:attr :shape-ref
|
||||
:val nil}]})
|
||||
shapes)
|
||||
|
||||
uchanges (map (fn [obj]
|
||||
{:type :mod-obj
|
||||
:page-id page-id
|
||||
:id (:id obj)
|
||||
:operations [{:type :set
|
||||
:attr :component-id
|
||||
:val (:component-id obj)}
|
||||
{:type :set
|
||||
:attr :component-file
|
||||
:val (:component-file obj)}
|
||||
{:type :set
|
||||
:attr :component-root?
|
||||
:val (:component-root? obj)}
|
||||
{:type :set
|
||||
:attr :shape-ref
|
||||
:val (:shape-ref obj)}]})
|
||||
shapes)]
|
||||
|
||||
(rx/of (dwc/commit-changes rchanges uchanges {:commit-local? true}))))))
|
||||
|
||||
(defn nav-to-component-file
|
||||
[file-id]
|
||||
(us/assert ::us/uuid file-id)
|
||||
(ptk/reify ::nav-to-component-file
|
||||
ptk/WatchEvent
|
||||
(watch [_ state stream]
|
||||
(let [file (get-in state [:workspace-libraries file-id])
|
||||
pparams {:project-id (:project-id file)
|
||||
:file-id (:id file)}
|
||||
qparams {:page-id (first (get-in file [:data :pages]))}]
|
||||
(st/emit! (rt/nav-new-window :workspace pparams qparams))))))
|
||||
|
||||
(defn ext-library-changed
|
||||
[file-id modified-at changes]
|
||||
(us/assert ::us/uuid file-id)
|
||||
(us/assert ::cp/changes changes)
|
||||
(ptk/reify ::ext-library-changed
|
||||
ptk/UpdateEvent
|
||||
(update [_ state]
|
||||
(-> state
|
||||
(assoc-in [:workspace-libraries file-id :modified-at] modified-at)
|
||||
(d/update-in-when [:workspace-libraries file-id :data]
|
||||
cp/process-changes changes)))))
|
||||
|
||||
(defn reset-component
|
||||
"Cancels all modifications in the shape with the given id, and all its children, in
|
||||
the current page. Set all attributes equal to the ones in the linked component,
|
||||
and untouched."
|
||||
[id]
|
||||
(us/assert ::us/uuid id)
|
||||
(ptk/reify ::reset-component
|
||||
ptk/WatchEvent
|
||||
(watch [_ state stream]
|
||||
;; ===== Uncomment this to debug =====
|
||||
;; (js/console.info "##### RESET-COMPONENT of shape" (str id))
|
||||
(let [[rchanges uchanges]
|
||||
(dwlh/generate-sync-shape-and-children-components (get state :current-page-id)
|
||||
nil
|
||||
id
|
||||
(get state :workspace-data)
|
||||
(get state :workspace-libraries)
|
||||
true)]
|
||||
;; ===== Uncomment this to debug =====
|
||||
;; (js/console.debug "rchanges" (clj->js rchanges))
|
||||
|
||||
(rx/of (dwc/commit-changes rchanges uchanges {:commit-local? true}))))))
|
||||
|
||||
(defn update-component
|
||||
"Modify the component linked to the shape with the given id, in the current page, so that
|
||||
all attributes of its shapes are equal to the shape and its children. Also set all attributes
|
||||
of the shape untouched."
|
||||
[id]
|
||||
(us/assert ::us/uuid id)
|
||||
(ptk/reify ::update-component
|
||||
ptk/WatchEvent
|
||||
(watch [_ state stream]
|
||||
;; ===== Uncomment this to debug =====
|
||||
;; (js/console.info "##### UPDATE-COMPONENT of shape" (str id))
|
||||
(let [page-id (:current-page-id state)
|
||||
objects (dwc/lookup-page-objects state page-id)
|
||||
shape (get objects id)
|
||||
file-id (get shape :component-file)
|
||||
|
||||
[rchanges uchanges]
|
||||
(dwlh/generate-sync-shape-inverse (get state :current-page-id)
|
||||
id
|
||||
(get state :workspace-data)
|
||||
(get state :workspace-libraries))]
|
||||
|
||||
;; ===== Uncomment this to debug =====
|
||||
;; (js/console.debug "rchanges" (clj->js rchanges))
|
||||
|
||||
(rx/of (dwc/commit-changes rchanges uchanges {:commit-local? true}))))))
|
||||
|
||||
(declare sync-file-2nd-stage)
|
||||
|
||||
(defn sync-file
|
||||
"Syhchronize the library file with the given id, with the current file.
|
||||
Walk through all shapes in all pages that use some color, typography or
|
||||
component of the library file, and copy the new values to the shapes.
|
||||
Do it also for shapes inside components of the local file library."
|
||||
[file-id]
|
||||
(us/assert (s/nilable ::us/uuid) file-id)
|
||||
(ptk/reify ::sync-file
|
||||
ptk/UpdateEvent
|
||||
(update [_ state]
|
||||
(if file-id
|
||||
(assoc-in state [:workspace-libraries file-id :synced-at] (dt/now))
|
||||
state))
|
||||
|
||||
ptk/WatchEvent
|
||||
(watch [_ state stream]
|
||||
;; ===== Uncomment this to debug =====
|
||||
(js/console.info "##### SYNC-FILE" (str (or file-id "local")))
|
||||
(let [library-changes [(dwlh/generate-sync-library :components file-id state)
|
||||
(dwlh/generate-sync-library :colors file-id state)
|
||||
(dwlh/generate-sync-library :typographies file-id state)]
|
||||
file-changes [(dwlh/generate-sync-file :components file-id state)
|
||||
(dwlh/generate-sync-file :colors file-id state)
|
||||
(dwlh/generate-sync-file :typographies file-id state)]
|
||||
rchanges (d/concat []
|
||||
(->> library-changes (remove nil?) (map first) (flatten))
|
||||
(->> file-changes (remove nil?) (map first) (flatten)))
|
||||
uchanges (d/concat []
|
||||
(->> library-changes (remove nil?) (map second) (flatten))
|
||||
(->> file-changes (remove nil?) (map second) (flatten)))]
|
||||
;; ===== Uncomment this to debug =====
|
||||
;; (js/console.debug "rchanges" (clj->js rchanges))
|
||||
(rx/concat
|
||||
(rx/of (dm/hide-tag :sync-dialog))
|
||||
(when rchanges
|
||||
(rx/of (dwc/commit-changes rchanges uchanges {:commit-local? true})))
|
||||
(when file-id
|
||||
(rp/mutation :update-sync
|
||||
{:file-id (get-in state [:workspace-file :id])
|
||||
:library-id file-id}))
|
||||
(when (some? library-changes)
|
||||
(rx/of (sync-file-2nd-stage file-id))))))))
|
||||
|
||||
(defn sync-file-2nd-stage
|
||||
"If some components have been modified, we need to launch another synchronization
|
||||
to update the instances of the changed components."
|
||||
;; TODO: this does not work if there are multiple nested components. Only the
|
||||
;; first level will be updated.
|
||||
;; To solve this properly, it would be better to launch another sync-file
|
||||
;; recursively. But for this not to cause an infinite loop, we need to
|
||||
;; implement updated-at at component level, to detect what components have
|
||||
;; not changed, and then not to apply sync and terminate the loop.
|
||||
[file-id]
|
||||
(us/assert (s/nilable ::us/uuid) file-id)
|
||||
(ptk/reify ::sync-file-2nd-stage
|
||||
ptk/WatchEvent
|
||||
(watch [_ state stream]
|
||||
;; ===== Uncomment this to debug =====
|
||||
(js/console.info "##### SYNC-FILE" (str (or file-id "local")) "(2nd stage)")
|
||||
(let [[rchanges1 uchanges1] (dwlh/generate-sync-file :components nil state)
|
||||
[rchanges2 uchanges2] (dwlh/generate-sync-library :components file-id state)
|
||||
rchanges (d/concat rchanges1 rchanges2)
|
||||
uchanges (d/concat uchanges1 uchanges2)]
|
||||
(when rchanges
|
||||
;; ===== Uncomment this to debug =====
|
||||
;; (js/console.debug "rchanges" (clj->js rchanges))
|
||||
(rx/of (dwc/commit-changes rchanges uchanges {:commit-local? true})))))))
|
||||
|
||||
(def ignore-sync
|
||||
(ptk/reify ::sync-file
|
||||
ptk/UpdateEvent
|
||||
(update [_ state]
|
||||
(assoc-in state [:workspace-file :ignore-sync-until] (dt/now)))
|
||||
|
||||
ptk/WatchEvent
|
||||
(watch [_ state stream]
|
||||
(rp/mutation :ignore-sync
|
||||
{:file-id (get-in state [:workspace-file :id])
|
||||
:date (dt/now)}))))
|
||||
|
||||
(defn notify-sync-file
|
||||
[file-id]
|
||||
(us/assert ::us/uuid file-id)
|
||||
(ptk/reify ::notify-sync-file
|
||||
ptk/WatchEvent
|
||||
(watch [_ state stream]
|
||||
(let [libraries-need-sync (filter #(> (:modified-at %) (:synced-at %))
|
||||
(vals (get state :workspace-libraries)))
|
||||
do-update #(do (apply st/emit! (map (fn [library]
|
||||
(sync-file (:id library)))
|
||||
libraries-need-sync))
|
||||
(st/emit! dm/hide))
|
||||
do-dismiss #(do (st/emit! ignore-sync)
|
||||
(st/emit! dm/hide))]
|
||||
(rx/of (dm/info-dialog
|
||||
(tr "workspace.updates.there-are-updates")
|
||||
:inline-actions
|
||||
[{:label (tr "workspace.updates.update")
|
||||
:callback do-update}
|
||||
{:label (tr "workspace.updates.dismiss")
|
||||
:callback do-dismiss}]
|
||||
:sync-dialog))))))
|
||||
|
||||
|
|
|
@ -33,9 +33,12 @@
|
|||
(declare has-asset-reference-fn)
|
||||
|
||||
(declare get-assets)
|
||||
(declare resolve-shapes-and-components)
|
||||
(declare generate-sync-shape-and-children-components)
|
||||
(declare generate-sync-shape-and-children-normal)
|
||||
(declare generate-sync-shape-and-children-nested)
|
||||
(declare generate-sync-shape-inverse)
|
||||
(declare generate-sync-shape-inverse-normal)
|
||||
(declare generate-sync-shape-inverse-nested)
|
||||
(declare generate-sync-shape<-component)
|
||||
(declare generate-sync-shape->component)
|
||||
(declare remove-component-and-ref)
|
||||
|
@ -55,23 +58,25 @@
|
|||
(assert (nil? (:component-id shape)))
|
||||
(assert (nil? (:component-file shape)))
|
||||
(assert (nil? (:shape-ref shape)))
|
||||
(let [update-new-shape (fn [new-shape original-shape]
|
||||
(let [;; Ensure that the component root is not an instance and
|
||||
;; it's no longer tied to a frame.
|
||||
update-new-shape (fn [new-shape original-shape]
|
||||
(cond-> new-shape
|
||||
true
|
||||
(assoc :frame-id nil)
|
||||
(-> (assoc :frame-id nil)
|
||||
(dissoc :component-root?))
|
||||
|
||||
(nil? (:parent-id new-shape))
|
||||
(dissoc :component-id
|
||||
:component-file
|
||||
:component-root?
|
||||
:shape-ref)))
|
||||
|
||||
;; Make the original shape an instance of the new component.
|
||||
;; If one of the original shape children already was a component
|
||||
;; instance, the 'instanceness' is copied into the new component.
|
||||
;; instance, maintain this instanceness untouched.
|
||||
update-original-shape (fn [original-shape new-shape]
|
||||
(cond-> original-shape
|
||||
true
|
||||
(nil? (:shape-ref original-shape))
|
||||
(-> (assoc :shape-ref (:id new-shape))
|
||||
(dissoc :touched))
|
||||
|
||||
|
@ -124,6 +129,7 @@
|
|||
"Generate changes to synchronize all shapes inside components of the current
|
||||
file library, that use the given type of asset of the given library."
|
||||
[asset-type library-id state]
|
||||
;; (js/console.info "--- SYNC local library " (str asset-type) " from library " (str (or library-id "nil")))
|
||||
(let [library-items
|
||||
(if (nil? library-id)
|
||||
(get-in state [:workspace-data asset-type])
|
||||
|
@ -176,7 +182,7 @@
|
|||
[asset-type library-id]
|
||||
(case asset-type
|
||||
:components
|
||||
(fn [shape] (and (:component-root? shape)
|
||||
(fn [shape] (and (:component-id shape)
|
||||
(= (:component-file shape) library-id)))
|
||||
|
||||
:colors
|
||||
|
@ -214,19 +220,12 @@
|
|||
|
||||
(defmethod generate-sync-shape :components
|
||||
[_ library-id state objects page-id component-id shape]
|
||||
(let [[all-shapes component root-component]
|
||||
(resolve-shapes-and-components shape
|
||||
objects
|
||||
state
|
||||
false)]
|
||||
|
||||
(generate-sync-shape-and-children-components shape
|
||||
all-shapes
|
||||
component
|
||||
root-component
|
||||
page-id
|
||||
component-id
|
||||
false)))
|
||||
(generate-sync-shape-and-children-components page-id
|
||||
component-id
|
||||
(:id shape)
|
||||
(get state :workspace-data)
|
||||
(get state :workspace-libraries)
|
||||
false))
|
||||
|
||||
(defn- generate-sync-text-shape [shape page-id component-id update-node]
|
||||
(let [old-content (:content shape)
|
||||
|
@ -328,37 +327,6 @@
|
|||
(get-in state [:workspace-libraries file-id :data :components]))]
|
||||
(get components component-id)))
|
||||
|
||||
(defn resolve-shapes-and-components
|
||||
"Get all shapes inside a component instance, and the component they are
|
||||
linked with. If follow-indirection? is true, and the shape corresponding
|
||||
to the root shape is also a component instance, follow the link and get
|
||||
the final component."
|
||||
[shape objects state follow-indirection?]
|
||||
(loop [all-shapes (cph/get-object-with-children (:id shape) objects)
|
||||
local-objects objects
|
||||
local-shape shape]
|
||||
|
||||
(let [root-shape (cph/get-root-shape local-shape local-objects)
|
||||
component (get-component state
|
||||
(get root-shape :component-file)
|
||||
(get root-shape :component-id))
|
||||
component-shape (get-in component [:objects (:shape-ref local-shape)])]
|
||||
|
||||
(if (or (nil? (:component-id component-shape))
|
||||
(not follow-indirection?))
|
||||
[all-shapes component component-shape]
|
||||
(let [resolve-indirection
|
||||
(fn [shape]
|
||||
(let [component-shape (get-in component [:objects (:shape-ref shape)])]
|
||||
(-> shape
|
||||
(assoc :shape-ref (:shape-ref component-shape))
|
||||
(d/assoc-when :component-id (:component-id component-shape))
|
||||
(d/assoc-when :component-file (:component-file component-shape)))))
|
||||
new-shapes (map resolve-indirection all-shapes)]
|
||||
(recur new-shapes
|
||||
(:objects component)
|
||||
component-shape))))))
|
||||
|
||||
(defn generate-sync-shape-and-children-components
|
||||
"Generate changes to synchronize one shape that the root of a component
|
||||
instance, and all its children, from the given component.
|
||||
|
@ -367,25 +335,108 @@
|
|||
be copied to this one.
|
||||
If reset? is true, all changed attributes will be copied and the 'touched'
|
||||
flags in the instance shape will be cleared."
|
||||
[root-shape all-shapes component root-component page-id component-id reset?]
|
||||
(loop [shapes (seq all-shapes)
|
||||
rchanges []
|
||||
uchanges []]
|
||||
(let [shape (first shapes)]
|
||||
(if (nil? shape)
|
||||
[page-id component-id shape-id local-file libraries reset?]
|
||||
(let [container (cph/get-container page-id component-id local-file)
|
||||
shape (cph/get-shape container shape-id)
|
||||
component (cph/get-component (:component-id shape)
|
||||
(:component-file shape)
|
||||
local-file
|
||||
libraries)
|
||||
root-shape shape
|
||||
root-component (cph/get-component-root component)]
|
||||
|
||||
(generate-sync-shape-and-children-normal page-id
|
||||
component-id
|
||||
container
|
||||
shape
|
||||
component
|
||||
root-shape
|
||||
root-component
|
||||
reset?)))
|
||||
|
||||
(defn- generate-sync-shape-and-children-normal
|
||||
[page-id component-id container shape component root-shape root-component reset?]
|
||||
(let [[rchanges uchanges]
|
||||
(generate-sync-shape<-component shape
|
||||
root-shape
|
||||
root-component
|
||||
component
|
||||
page-id
|
||||
component-id
|
||||
reset?)
|
||||
|
||||
children-ids (get shape :shapes [])]
|
||||
|
||||
(loop [children-ids (seq children-ids)
|
||||
rchanges rchanges
|
||||
uchanges uchanges]
|
||||
(let [child-id (first children-ids)]
|
||||
(if (nil? child-id)
|
||||
[rchanges uchanges]
|
||||
(let [child-shape (cph/get-shape container child-id)
|
||||
|
||||
[child-rchanges child-uchanges]
|
||||
(if (nil? (:component-id child-shape))
|
||||
(generate-sync-shape-and-children-normal page-id
|
||||
component-id
|
||||
container
|
||||
child-shape
|
||||
component
|
||||
root-shape
|
||||
root-component
|
||||
reset?)
|
||||
(generate-sync-shape-and-children-nested page-id
|
||||
component-id
|
||||
container
|
||||
child-shape
|
||||
component
|
||||
root-shape
|
||||
root-component
|
||||
reset?))]
|
||||
(recur (next children-ids)
|
||||
(d/concat rchanges child-rchanges)
|
||||
(d/concat uchanges child-uchanges))))))))
|
||||
|
||||
(defn- generate-sync-shape-and-children-nested
|
||||
[page-id component-id container shape component root-shape root-component reset?]
|
||||
(let [component-shape (d/seek #(= (:shape-ref %)
|
||||
(:shape-ref shape))
|
||||
(vals (:objects component)))
|
||||
|
||||
[rchanges uchanges]
|
||||
(let [[shape-rchanges shape-uchanges]
|
||||
(generate-sync-shape<-component
|
||||
shape
|
||||
root-shape
|
||||
root-component
|
||||
component
|
||||
page-id
|
||||
component-id
|
||||
reset?)]
|
||||
(recur (next shapes)
|
||||
(d/concat rchanges shape-rchanges)
|
||||
(d/concat uchanges shape-uchanges)))))))
|
||||
(update-attrs shape
|
||||
component-shape
|
||||
root-shape
|
||||
root-component
|
||||
page-id
|
||||
component-id
|
||||
{:omit-touched? false
|
||||
:reset-touched? false
|
||||
:set-touched? false
|
||||
:copy-touched? true})
|
||||
|
||||
children-ids (get shape :shapes [])]
|
||||
|
||||
(loop [children-ids (seq children-ids)
|
||||
rchanges rchanges
|
||||
uchanges uchanges]
|
||||
(let [child-id (first children-ids)]
|
||||
(if (nil? child-id)
|
||||
[rchanges uchanges]
|
||||
(let [child-shape (cph/get-shape container child-id)
|
||||
|
||||
[child-rchanges child-uchanges]
|
||||
(generate-sync-shape-and-children-nested page-id
|
||||
component-id
|
||||
container
|
||||
child-shape
|
||||
component
|
||||
root-shape
|
||||
root-component
|
||||
reset?)]
|
||||
(recur (next children-ids)
|
||||
(d/concat rchanges child-rchanges)
|
||||
(d/concat uchanges child-uchanges))))))))
|
||||
|
||||
(defn- generate-sync-shape-inverse
|
||||
"Generate changes to update the component a shape is linked to, from
|
||||
|
@ -395,23 +446,94 @@
|
|||
shapes.
|
||||
And if the component shapes are, in turn, instances of a second component,
|
||||
their 'touched' flags will be set accordingly."
|
||||
[root-shape all-shapes component root-component page-id]
|
||||
(loop [shapes (seq all-shapes)
|
||||
rchanges []
|
||||
uchanges []]
|
||||
(let [shape (first shapes)]
|
||||
(if (nil? shape)
|
||||
[page-id shape-id local-file libraries]
|
||||
(let [page (cph/get-container page-id nil local-file)
|
||||
shape (cph/get-shape page shape-id)
|
||||
component (cph/get-component (:component-id shape)
|
||||
(:component-file shape)
|
||||
local-file
|
||||
libraries)
|
||||
root-shape shape
|
||||
root-component (cph/get-component-root component)]
|
||||
|
||||
(generate-sync-shape-inverse-normal page
|
||||
shape
|
||||
component
|
||||
root-shape
|
||||
root-component)))
|
||||
|
||||
(defn- generate-sync-shape-inverse-normal
|
||||
[page shape component root-shape root-component]
|
||||
(let [[rchanges uchanges]
|
||||
(generate-sync-shape->component shape
|
||||
root-shape
|
||||
root-component
|
||||
component
|
||||
(:id page))
|
||||
|
||||
children-ids (get shape :shapes [])]
|
||||
|
||||
(loop [children-ids (seq children-ids)
|
||||
rchanges rchanges
|
||||
uchanges uchanges]
|
||||
(let [child-id (first children-ids)]
|
||||
(if (nil? child-id)
|
||||
[rchanges uchanges]
|
||||
(let [child-shape (cph/get-shape page child-id)
|
||||
|
||||
[child-rchanges child-uchanges]
|
||||
(if (nil? (:component-id child-shape))
|
||||
(generate-sync-shape-inverse-normal page
|
||||
child-shape
|
||||
component
|
||||
root-shape
|
||||
root-component)
|
||||
(generate-sync-shape-inverse-nested page
|
||||
child-shape
|
||||
component
|
||||
root-shape
|
||||
root-component))]
|
||||
(recur (next children-ids)
|
||||
(d/concat rchanges child-rchanges)
|
||||
(d/concat uchanges child-uchanges))))))))
|
||||
|
||||
(defn- generate-sync-shape-inverse-nested
|
||||
[page shape component root-shape root-component]
|
||||
(let [component-shape (d/seek #(= (:shape-ref %)
|
||||
(:shape-ref shape))
|
||||
(vals (:objects component)))
|
||||
|
||||
[rchanges uchanges]
|
||||
(let [[shape-rchanges shape-uchanges]
|
||||
(generate-sync-shape->component
|
||||
shape
|
||||
root-shape
|
||||
root-component
|
||||
component
|
||||
page-id)]
|
||||
(recur (next shapes)
|
||||
(d/concat rchanges shape-rchanges)
|
||||
(d/concat uchanges shape-uchanges)))))))
|
||||
(update-attrs component-shape
|
||||
shape
|
||||
root-component
|
||||
root-shape
|
||||
nil
|
||||
(:id component)
|
||||
{:omit-touched? false
|
||||
:reset-touched? false
|
||||
:set-touched? false
|
||||
:copy-touched? true})
|
||||
|
||||
children-ids (get shape :shapes [])]
|
||||
|
||||
(loop [children-ids (seq children-ids)
|
||||
rchanges rchanges
|
||||
uchanges uchanges]
|
||||
(let [child-id (first children-ids)]
|
||||
(if (nil? child-id)
|
||||
[rchanges uchanges]
|
||||
(let [child-shape (cph/get-shape page child-id)
|
||||
|
||||
[child-rchanges child-uchanges]
|
||||
(generate-sync-shape-inverse-nested page
|
||||
child-shape
|
||||
component
|
||||
root-shape
|
||||
root-component)]
|
||||
(recur (next children-ids)
|
||||
(d/concat rchanges child-rchanges)
|
||||
(d/concat uchanges child-uchanges))))))))
|
||||
|
||||
(defn- generate-sync-shape<-component
|
||||
"Generate changes to synchronize one shape that is linked to other shape
|
||||
|
@ -552,13 +674,17 @@
|
|||
If set-touched? is true, the corresponding 'touched' flags will be
|
||||
set in dest shape if they are different than their current values."
|
||||
[dest-shape origin-shape dest-root origin-root page-id component-id
|
||||
{:keys [omit-touched? reset-touched? set-touched?] :as options}]
|
||||
{:keys [omit-touched? reset-touched? set-touched? copy-touched?]
|
||||
:as options :or {omit-touched? false
|
||||
reset-touched? false
|
||||
set-touched? false
|
||||
copy-touched? false}}]
|
||||
|
||||
;; === Uncomment this to debug synchronization ===
|
||||
;; (println "SYNC"
|
||||
;; "[C]" (:name origin-shape)
|
||||
;; (:name origin-shape)
|
||||
;; "->"
|
||||
;; (if page-id "[W]" ["C"])
|
||||
;; (if page-id "[W]" "[C]")
|
||||
;; (:name dest-shape)
|
||||
;; (str options))
|
||||
|
||||
|
@ -582,16 +708,24 @@
|
|||
|
||||
(let [attr (first attrs)]
|
||||
(if (nil? attr)
|
||||
(let [roperations (if reset-touched?
|
||||
(let [roperations (cond
|
||||
reset-touched?
|
||||
(conj roperations
|
||||
{:type :set-touched
|
||||
:touched nil})
|
||||
copy-touched?
|
||||
(conj roperations
|
||||
{:type :set-touched
|
||||
:touched (:touched origin-shape)})
|
||||
:else
|
||||
roperations)
|
||||
|
||||
uoperations (if reset-touched?
|
||||
uoperations (cond
|
||||
(or reset-touched? copy-touched?)
|
||||
(conj uoperations
|
||||
{:type :set-touched
|
||||
:touched (:touched dest-shape)})
|
||||
:else
|
||||
uoperations)
|
||||
|
||||
rchanges [(d/without-nils {:type :mod-obj
|
||||
|
|
|
@ -85,8 +85,9 @@
|
|||
(logjs "state")))))
|
||||
|
||||
(defn ^:export dump-tree
|
||||
([] (dump-tree false))
|
||||
([show-touched]
|
||||
([] (dump-tree false false))
|
||||
([show-ids] (dump-tree show-ids false))
|
||||
([show-ids show-touched]
|
||||
(let [page-id (get @state :current-page-id)
|
||||
objects (get-in @state [:workspace-data :pages-index page-id :objects])
|
||||
components (get-in @state [:workspace-data :components])
|
||||
|
@ -98,6 +99,7 @@
|
|||
(println (str/pad (str (str/repeat " " level)
|
||||
(:name shape)
|
||||
(when (seq (:touched shape)) "*")
|
||||
(when show-ids (str/format " <%s>" (:id shape))))
|
||||
{:length 20
|
||||
:type :right})
|
||||
(show-component shape objects))
|
||||
|
@ -107,7 +109,7 @@
|
|||
(str (:touched shape)))))
|
||||
(when (:shapes shape)
|
||||
(dorun (for [shape-id (:shapes shape)]
|
||||
(show-shape shape-id (inc level) objects)))))))
|
||||
(show-shape shape-id (inc level) objects))))))
|
||||
|
||||
(show-component [shape objects]
|
||||
(if (nil? (:shape-ref shape))
|
||||
|
@ -129,7 +131,8 @@
|
|||
(when component-file (str/format "<%s> " (:name component-file)))
|
||||
(:name component-shape)
|
||||
(if (or (:component-root? shape)
|
||||
(nil? (:component-id shape)))
|
||||
(nil? (:component-id shape))
|
||||
true)
|
||||
""
|
||||
(let [component-id (:component-id shape)
|
||||
component-file-id (:component-file shape)
|
||||
|
|
Loading…
Add table
Reference in a new issue