mirror of
https://github.com/penpot/penpot.git
synced 2025-06-03 19:22:18 +02: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
|
(defn get-root-shape
|
||||||
"Get the root shape linked to a component for this shape, if any"
|
"Get the root shape linked to a component for this shape, if any"
|
||||||
[shape objects]
|
[shape objects]
|
||||||
(if (:component-root? shape)
|
(if (:component-id shape)
|
||||||
shape
|
shape
|
||||||
(if-let [parent-id (:parent-id shape)]
|
(if-let [parent-id (:parent-id shape)]
|
||||||
(get-root-shape (get objects (:parent-id shape))
|
(get-root-shape (get objects (:parent-id shape))
|
||||||
objects)
|
objects)
|
||||||
nil)))
|
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
|
(defn get-children
|
||||||
"Retrieve all children ids recursively for a given object"
|
"Retrieve all children ids recursively for a given object"
|
||||||
[id objects]
|
[id objects]
|
||||||
|
|
|
@ -20,18 +20,20 @@
|
||||||
(defn show
|
(defn show
|
||||||
([props]
|
([props]
|
||||||
(show (uuid/next) (:type props) props))
|
(show (uuid/next) (:type props) props))
|
||||||
([type props] (show (uuid/next) type props))
|
([type props]
|
||||||
|
(show (uuid/next) type props))
|
||||||
([id type props]
|
([id type props]
|
||||||
(ptk/reify ::show-modal
|
(ptk/reify ::show-modal
|
||||||
ptk/UpdateEvent
|
ptk/UpdateEvent
|
||||||
(update [_ state]
|
(update [_ state]
|
||||||
(assoc state ::modal {:id id
|
(assoc state ::modal {:id id
|
||||||
:type type
|
:type type
|
||||||
:props props
|
:props props
|
||||||
:allow-click-outside false})))))
|
:allow-click-outside false})))))
|
||||||
|
|
||||||
(defn update-props
|
(defn update-props
|
||||||
([type props]
|
([type props]
|
||||||
(ptk/reify ::show-modal
|
(ptk/reify ::update-modal-props
|
||||||
ptk/UpdateEvent
|
ptk/UpdateEvent
|
||||||
(update [_ state]
|
(update [_ state]
|
||||||
(cond-> state
|
(cond-> state
|
||||||
|
|
|
@ -125,423 +125,6 @@
|
||||||
:object prev}]
|
:object prev}]
|
||||||
(rx/of (dwc/commit-changes [rchg] [uchg] {:commit-local? true}))))))
|
(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
|
(defn add-typography
|
||||||
([typography] (add-typography typography true))
|
([typography] (add-typography typography true))
|
||||||
([typography edit?]
|
([typography edit?]
|
||||||
|
@ -586,3 +169,417 @@
|
||||||
uchg {:type :add-typography
|
uchg {:type :add-typography
|
||||||
:typography prev}]
|
:typography prev}]
|
||||||
(rx/of (dwc/commit-changes [rchg] [uchg] {:commit-local? true}))))))
|
(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 has-asset-reference-fn)
|
||||||
|
|
||||||
(declare get-assets)
|
(declare get-assets)
|
||||||
(declare resolve-shapes-and-components)
|
|
||||||
(declare generate-sync-shape-and-children-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)
|
||||||
|
(declare generate-sync-shape-inverse-normal)
|
||||||
|
(declare generate-sync-shape-inverse-nested)
|
||||||
(declare generate-sync-shape<-component)
|
(declare generate-sync-shape<-component)
|
||||||
(declare generate-sync-shape->component)
|
(declare generate-sync-shape->component)
|
||||||
(declare remove-component-and-ref)
|
(declare remove-component-and-ref)
|
||||||
|
@ -55,23 +58,25 @@
|
||||||
(assert (nil? (:component-id shape)))
|
(assert (nil? (:component-id shape)))
|
||||||
(assert (nil? (:component-file shape)))
|
(assert (nil? (:component-file shape)))
|
||||||
(assert (nil? (:shape-ref 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
|
(cond-> new-shape
|
||||||
true
|
true
|
||||||
(assoc :frame-id nil)
|
(-> (assoc :frame-id nil)
|
||||||
|
(dissoc :component-root?))
|
||||||
|
|
||||||
(nil? (:parent-id new-shape))
|
(nil? (:parent-id new-shape))
|
||||||
(dissoc :component-id
|
(dissoc :component-id
|
||||||
:component-file
|
:component-file
|
||||||
:component-root?
|
|
||||||
:shape-ref)))
|
:shape-ref)))
|
||||||
|
|
||||||
;; Make the original shape an instance of the new component.
|
;; Make the original shape an instance of the new component.
|
||||||
;; If one of the original shape children already was a 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]
|
update-original-shape (fn [original-shape new-shape]
|
||||||
(cond-> original-shape
|
(cond-> original-shape
|
||||||
true
|
(nil? (:shape-ref original-shape))
|
||||||
(-> (assoc :shape-ref (:id new-shape))
|
(-> (assoc :shape-ref (:id new-shape))
|
||||||
(dissoc :touched))
|
(dissoc :touched))
|
||||||
|
|
||||||
|
@ -124,6 +129,7 @@
|
||||||
"Generate changes to synchronize all shapes inside components of the current
|
"Generate changes to synchronize all shapes inside components of the current
|
||||||
file library, that use the given type of asset of the given library."
|
file library, that use the given type of asset of the given library."
|
||||||
[asset-type library-id state]
|
[asset-type library-id state]
|
||||||
|
;; (js/console.info "--- SYNC local library " (str asset-type) " from library " (str (or library-id "nil")))
|
||||||
(let [library-items
|
(let [library-items
|
||||||
(if (nil? library-id)
|
(if (nil? library-id)
|
||||||
(get-in state [:workspace-data asset-type])
|
(get-in state [:workspace-data asset-type])
|
||||||
|
@ -176,7 +182,7 @@
|
||||||
[asset-type library-id]
|
[asset-type library-id]
|
||||||
(case asset-type
|
(case asset-type
|
||||||
:components
|
:components
|
||||||
(fn [shape] (and (:component-root? shape)
|
(fn [shape] (and (:component-id shape)
|
||||||
(= (:component-file shape) library-id)))
|
(= (:component-file shape) library-id)))
|
||||||
|
|
||||||
:colors
|
:colors
|
||||||
|
@ -214,19 +220,12 @@
|
||||||
|
|
||||||
(defmethod generate-sync-shape :components
|
(defmethod generate-sync-shape :components
|
||||||
[_ library-id state objects page-id component-id shape]
|
[_ library-id state objects page-id component-id shape]
|
||||||
(let [[all-shapes component root-component]
|
(generate-sync-shape-and-children-components page-id
|
||||||
(resolve-shapes-and-components shape
|
component-id
|
||||||
objects
|
(:id shape)
|
||||||
state
|
(get state :workspace-data)
|
||||||
false)]
|
(get state :workspace-libraries)
|
||||||
|
false))
|
||||||
(generate-sync-shape-and-children-components shape
|
|
||||||
all-shapes
|
|
||||||
component
|
|
||||||
root-component
|
|
||||||
page-id
|
|
||||||
component-id
|
|
||||||
false)))
|
|
||||||
|
|
||||||
(defn- generate-sync-text-shape [shape page-id component-id update-node]
|
(defn- generate-sync-text-shape [shape page-id component-id update-node]
|
||||||
(let [old-content (:content shape)
|
(let [old-content (:content shape)
|
||||||
|
@ -328,37 +327,6 @@
|
||||||
(get-in state [:workspace-libraries file-id :data :components]))]
|
(get-in state [:workspace-libraries file-id :data :components]))]
|
||||||
(get components component-id)))
|
(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
|
(defn generate-sync-shape-and-children-components
|
||||||
"Generate changes to synchronize one shape that the root of a component
|
"Generate changes to synchronize one shape that the root of a component
|
||||||
instance, and all its children, from the given component.
|
instance, and all its children, from the given component.
|
||||||
|
@ -367,25 +335,108 @@
|
||||||
be copied to this one.
|
be copied to this one.
|
||||||
If reset? is true, all changed attributes will be copied and the 'touched'
|
If reset? is true, all changed attributes will be copied and the 'touched'
|
||||||
flags in the instance shape will be cleared."
|
flags in the instance shape will be cleared."
|
||||||
[root-shape all-shapes component root-component page-id component-id reset?]
|
[page-id component-id shape-id local-file libraries reset?]
|
||||||
(loop [shapes (seq all-shapes)
|
(let [container (cph/get-container page-id component-id local-file)
|
||||||
rchanges []
|
shape (cph/get-shape container shape-id)
|
||||||
uchanges []]
|
component (cph/get-component (:component-id shape)
|
||||||
(let [shape (first shapes)]
|
(:component-file shape)
|
||||||
(if (nil? 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]
|
[rchanges uchanges]
|
||||||
(let [[shape-rchanges shape-uchanges]
|
(update-attrs shape
|
||||||
(generate-sync-shape<-component
|
component-shape
|
||||||
shape
|
root-shape
|
||||||
root-shape
|
root-component
|
||||||
root-component
|
page-id
|
||||||
component
|
component-id
|
||||||
page-id
|
{:omit-touched? false
|
||||||
component-id
|
:reset-touched? false
|
||||||
reset?)]
|
:set-touched? false
|
||||||
(recur (next shapes)
|
:copy-touched? true})
|
||||||
(d/concat rchanges shape-rchanges)
|
|
||||||
(d/concat uchanges shape-uchanges)))))))
|
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
|
(defn- generate-sync-shape-inverse
|
||||||
"Generate changes to update the component a shape is linked to, from
|
"Generate changes to update the component a shape is linked to, from
|
||||||
|
@ -395,23 +446,94 @@
|
||||||
shapes.
|
shapes.
|
||||||
And if the component shapes are, in turn, instances of a second component,
|
And if the component shapes are, in turn, instances of a second component,
|
||||||
their 'touched' flags will be set accordingly."
|
their 'touched' flags will be set accordingly."
|
||||||
[root-shape all-shapes component root-component page-id]
|
[page-id shape-id local-file libraries]
|
||||||
(loop [shapes (seq all-shapes)
|
(let [page (cph/get-container page-id nil local-file)
|
||||||
rchanges []
|
shape (cph/get-shape page shape-id)
|
||||||
uchanges []]
|
component (cph/get-component (:component-id shape)
|
||||||
(let [shape (first shapes)]
|
(:component-file shape)
|
||||||
(if (nil? 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]
|
[rchanges uchanges]
|
||||||
(let [[shape-rchanges shape-uchanges]
|
(update-attrs component-shape
|
||||||
(generate-sync-shape->component
|
shape
|
||||||
shape
|
root-component
|
||||||
root-shape
|
root-shape
|
||||||
root-component
|
nil
|
||||||
component
|
(:id component)
|
||||||
page-id)]
|
{:omit-touched? false
|
||||||
(recur (next shapes)
|
:reset-touched? false
|
||||||
(d/concat rchanges shape-rchanges)
|
:set-touched? false
|
||||||
(d/concat uchanges shape-uchanges)))))))
|
: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
|
(defn- generate-sync-shape<-component
|
||||||
"Generate changes to synchronize one shape that is linked to other shape
|
"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
|
If set-touched? is true, the corresponding 'touched' flags will be
|
||||||
set in dest shape if they are different than their current values."
|
set in dest shape if they are different than their current values."
|
||||||
[dest-shape origin-shape dest-root origin-root page-id component-id
|
[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 ===
|
;; === Uncomment this to debug synchronization ===
|
||||||
;; (println "SYNC"
|
;; (println "SYNC"
|
||||||
;; "[C]" (:name origin-shape)
|
;; (:name origin-shape)
|
||||||
;; "->"
|
;; "->"
|
||||||
;; (if page-id "[W]" ["C"])
|
;; (if page-id "[W]" "[C]")
|
||||||
;; (:name dest-shape)
|
;; (:name dest-shape)
|
||||||
;; (str options))
|
;; (str options))
|
||||||
|
|
||||||
|
@ -582,16 +708,24 @@
|
||||||
|
|
||||||
(let [attr (first attrs)]
|
(let [attr (first attrs)]
|
||||||
(if (nil? attr)
|
(if (nil? attr)
|
||||||
(let [roperations (if reset-touched?
|
(let [roperations (cond
|
||||||
|
reset-touched?
|
||||||
(conj roperations
|
(conj roperations
|
||||||
{:type :set-touched
|
{:type :set-touched
|
||||||
:touched nil})
|
:touched nil})
|
||||||
|
copy-touched?
|
||||||
|
(conj roperations
|
||||||
|
{:type :set-touched
|
||||||
|
:touched (:touched origin-shape)})
|
||||||
|
:else
|
||||||
roperations)
|
roperations)
|
||||||
|
|
||||||
uoperations (if reset-touched?
|
uoperations (cond
|
||||||
|
(or reset-touched? copy-touched?)
|
||||||
(conj uoperations
|
(conj uoperations
|
||||||
{:type :set-touched
|
{:type :set-touched
|
||||||
:touched (:touched dest-shape)})
|
:touched (:touched dest-shape)})
|
||||||
|
:else
|
||||||
uoperations)
|
uoperations)
|
||||||
|
|
||||||
rchanges [(d/without-nils {:type :mod-obj
|
rchanges [(d/without-nils {:type :mod-obj
|
||||||
|
|
|
@ -85,8 +85,9 @@
|
||||||
(logjs "state")))))
|
(logjs "state")))))
|
||||||
|
|
||||||
(defn ^:export dump-tree
|
(defn ^:export dump-tree
|
||||||
([] (dump-tree false))
|
([] (dump-tree false false))
|
||||||
([show-touched]
|
([show-ids] (dump-tree show-ids false))
|
||||||
|
([show-ids show-touched]
|
||||||
(let [page-id (get @state :current-page-id)
|
(let [page-id (get @state :current-page-id)
|
||||||
objects (get-in @state [:workspace-data :pages-index page-id :objects])
|
objects (get-in @state [:workspace-data :pages-index page-id :objects])
|
||||||
components (get-in @state [:workspace-data :components])
|
components (get-in @state [:workspace-data :components])
|
||||||
|
@ -98,6 +99,7 @@
|
||||||
(println (str/pad (str (str/repeat " " level)
|
(println (str/pad (str (str/repeat " " level)
|
||||||
(:name shape)
|
(:name shape)
|
||||||
(when (seq (:touched shape)) "*")
|
(when (seq (:touched shape)) "*")
|
||||||
|
(when show-ids (str/format " <%s>" (:id shape))))
|
||||||
{:length 20
|
{:length 20
|
||||||
:type :right})
|
:type :right})
|
||||||
(show-component shape objects))
|
(show-component shape objects))
|
||||||
|
@ -107,7 +109,7 @@
|
||||||
(str (:touched shape)))))
|
(str (:touched shape)))))
|
||||||
(when (:shapes shape)
|
(when (:shapes shape)
|
||||||
(dorun (for [shape-id (: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]
|
(show-component [shape objects]
|
||||||
(if (nil? (:shape-ref shape))
|
(if (nil? (:shape-ref shape))
|
||||||
|
@ -129,7 +131,8 @@
|
||||||
(when component-file (str/format "<%s> " (:name component-file)))
|
(when component-file (str/format "<%s> " (:name component-file)))
|
||||||
(:name component-shape)
|
(:name component-shape)
|
||||||
(if (or (:component-root? shape)
|
(if (or (:component-root? shape)
|
||||||
(nil? (:component-id shape)))
|
(nil? (:component-id shape))
|
||||||
|
true)
|
||||||
""
|
""
|
||||||
(let [component-id (:component-id shape)
|
(let [component-id (:component-id shape)
|
||||||
component-file-id (:component-file shape)
|
component-file-id (:component-file shape)
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue