Merge pull request #2108 from penpot/hiru-main-instance

Components v2 (first PR)
This commit is contained in:
Andrey Antukh 2022-08-01 13:25:06 +02:00 committed by GitHub
commit 18970cb233
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
101 changed files with 3212 additions and 1262 deletions

View file

@ -63,6 +63,11 @@
flags (sequence (map keyword) (str/words flags))]
(flags/parse flags/default default-flags flags)))
(defn- parse-features
[global]
(when-let [features-str (obj/get global "penpotFeatures")]
(map keyword (str/words features-str))))
(defn- parse-version
[global]
(-> (obj/get global "penpotVersion")
@ -88,6 +93,7 @@
(def build-date (parse-build-date global))
(def flags (atom (parse-flags global)))
(def features (atom (parse-features global)))
(def version (atom (parse-version global)))
(def target (atom (parse-target global)))
(def browser (atom (parse-browser)))

View file

@ -16,7 +16,9 @@
[app.main.sentry :as sentry]
[app.main.store :as st]
[app.main.ui :as ui]
[app.main.ui.alert]
[app.main.ui.confirm]
[app.main.ui.delete-shared]
[app.main.ui.modal :refer [modal]]
[app.main.ui.routes :as rt]
[app.main.worker :as worker]

View file

@ -13,6 +13,7 @@
[app.main.data.fonts :as df]
[app.main.data.media :as di]
[app.main.data.users :as du]
[app.main.features :as features]
[app.main.repo :as rp]
[app.util.i18n :as i18n :refer [tr]]
[app.util.router :as rt]
@ -246,6 +247,32 @@
(->> (rp/query :team-shared-files {:team-id team-id})
(rx/map shared-files-fetched))))))
;; --- EVENT: Get files that use this shared-file
(defn clean-temp-shared
[]
(ptk/reify ::clean-temp-shared
ptk/UpdateEvent
(update [_ state]
(assoc-in state [:dashboard-local :files-with-shared] nil))))
(defn library-using-files-fetched
[files]
(ptk/reify ::library-using-files-fetched
ptk/UpdateEvent
(update [_ state]
(let [files (d/index-by :id files)]
(assoc-in state [:dashboard-local :files-with-shared] files)))))
(defn fetch-library-using-files
[file]
(ptk/reify ::fetch-library-using-files
ptk/WatchEvent
(watch [_ _ _]
(let [file-id (:id file)]
(->> (rp/query :library-using-files {:file-id file-id})
(rx/map library-using-files-fetched))))))
;; --- EVENT: recent-files
(defn recent-files-fetched
@ -718,12 +745,13 @@
(-deref [_] {:project-id project-id})
ptk/WatchEvent
(watch [it _ _]
(watch [it state _]
(let [{:keys [on-success on-error]
:or {on-success identity
on-error rx/throw}} (meta params)
name (name (gensym (str (tr "dashboard.new-file-prefix") " ")))
params (assoc params :name name)]
components-v2 (features/active-feature? state :components-v2)
params (assoc params :name name :components-v2 components-v2)]
(->> (rp/mutation! :create-file params)
(rx/tap on-success)

View file

@ -10,9 +10,11 @@
[app.common.geom.point :as gpt]
[app.common.pages.helpers :as cph]
[app.common.spec :as us]
[app.common.types.shape-tree :as ctt]
[app.common.types.shape.interactions :as ctsi]
[app.main.data.comments :as dcm]
[app.main.data.fonts :as df]
[app.main.features :as features]
[app.main.repo :as rp]
[app.util.globals :as ug]
[app.util.router :as rt]
@ -99,9 +101,15 @@
(us/assert ::fetch-bundle-params params)
(ptk/reify ::fetch-file
ptk/WatchEvent
(watch [_ _ _]
(let [params' (cond-> {:file-id file-id}
(uuid? share-id) (assoc :share-id share-id))]
(watch [_ state _]
(let [components-v2 (features/active-feature? state :components-v2)
params' (cond-> {:file-id file-id}
(uuid? share-id)
(assoc :share-id share-id)
:always
(assoc :components-v2 components-v2))]
(->> (rp/query :view-only-bundle params')
(rx/mapcat
(fn [{:keys [fonts] :as bundle}]
@ -116,8 +124,8 @@
(map (fn [page-id]
(let [data (get-in file [:data :pages-index page-id])]
[page-id (assoc data
:frames (cph/get-viewer-frames (:objects data))
:all-frames (cph/get-viewer-frames (:objects data) {:all-frames? true}))])))
:frames (ctt/get-viewer-frames (:objects data))
:all-frames (ctt/get-viewer-frames (:objects data) {:all-frames? true}))])))
(into {}))]
(ptk/reify ::bundle-fetched

View file

@ -14,13 +14,13 @@
[app.common.geom.proportions :as gpr]
[app.common.geom.shapes :as gsh]
[app.common.math :as mth]
[app.common.pages :as cp]
[app.common.pages.changes-builder :as pcb]
[app.common.pages.helpers :as cph]
[app.common.spec :as us]
[app.common.text :as txt]
[app.common.transit :as t]
[app.common.types.shape :as cts]
[app.common.types.shape-tree :as ctst]
[app.common.uuid :as uuid]
[app.config :as cfg]
[app.main.data.events :as ev]
@ -59,7 +59,6 @@
[app.util.globals :as ug]
[app.util.http :as http]
[app.util.i18n :as i18n]
[app.util.names :as un]
[app.util.router :as rt]
[app.util.timers :as tm]
[app.util.webapi :as wapi]
@ -157,7 +156,7 @@
:workspace-project project
:workspace-file (assoc file :initialized true)
:workspace-data (-> (:data file)
(cph/start-object-indices)
(ctst/start-object-indices)
;; DEBUG: Uncomment this to try out migrations in local without changing
;; the version number
#_(assoc :version 17)
@ -215,7 +214,8 @@
(watch [_ state _]
(if (contains? (get-in state [:workspace-data :pages-index]) page-id)
(rx/of (dwp/preload-data-uris)
(dwth/watch-state-changes))
(dwth/watch-state-changes)
(dwl/watch-component-changes))
(let [default-page-id (get-in state [:workspace-data :pages 0])]
(rx/of (go-to-page default-page-id)))))
@ -270,8 +270,8 @@
ptk/WatchEvent
(watch [it state _]
(let [pages (get-in state [:workspace-data :pages-index])
unames (un/retrieve-used-names pages)
name (un/generate-unique-name unames "Page-1")
unames (ctst/retrieve-used-names pages)
name (ctst/generate-unique-name unames "Page-1")
changes (-> (pcb/empty-changes it)
(pcb/add-empty-page id name))]
@ -285,9 +285,9 @@
(watch [it state _]
(let [id (uuid/next)
pages (get-in state [:workspace-data :pages-index])
unames (un/retrieve-used-names pages)
unames (ctst/retrieve-used-names pages)
page (get-in state [:workspace-data :pages-index page-id])
name (un/generate-unique-name unames (:name page))
name (ctst/generate-unique-name unames (:name page))
no_thumbnails_objects (->> (:objects page)
(d/mapm (fn [_ val] (dissoc val :use-for-thumbnail?))))
@ -991,7 +991,7 @@
(let [selected (wsh/lookup-selected state)
pages (-> state :workspace-data :pages-index vals)
get-frames (fn [{:keys [objects id] :as page}]
(->> (cph/get-frames objects)
(->> (ctst/get-frames objects)
(sequence
(comp (filter :use-for-thumbnail?)
(map :id)
@ -1223,7 +1223,7 @@
;; selected and its parents
objects (cph/selected-subtree objects selected)
selected (->> (cph/sort-z-index objects selected)
selected (->> (ctst/sort-z-index objects selected)
(into (d/ordered-set)))]
(assoc data :selected selected)))
@ -1478,7 +1478,7 @@
[frame-id frame-id delta])
(empty? page-selected)
(let [frame-id (cph/frame-id-by-position page-objects mouse-pos)
(let [frame-id (ctst/frame-id-by-position page-objects mouse-pos)
delta (gpt/subtract mouse-pos orig-pos)]
[frame-id frame-id delta])
@ -1590,8 +1590,8 @@
height 16
page-id (:current-page-id state)
frame-id (-> (wsh/lookup-page-objects state page-id)
(cph/frame-id-by-position @ms/mouse-position))
shape (cp/setup-rect-selrect
(ctst/frame-id-by-position @ms/mouse-position))
shape (cts/setup-rect-selrect
{:id id
:type :text
:name "Text"
@ -1681,12 +1681,12 @@
(let [srect (gsh/selection-rect selected-objs)
frame-id (get-in objects [(first selected) :frame-id])
parent-id (get-in objects [(first selected) :parent-id])
shape (-> (cp/make-minimal-shape :frame)
shape (-> (cts/make-minimal-shape :frame)
(merge {:x (:x srect) :y (:y srect) :width (:width srect) :height (:height srect)})
(assoc :frame-id frame-id :parent-id parent-id)
(cond-> (not= frame-id uuid/zero)
(assoc :fills [] :hide-in-viewer true))
(cp/setup-rect-selrect))]
(cts/setup-rect-selrect))]
(rx/of
(dwu/start-undo-transaction)
(dwsh/add-shape shape)

View file

@ -11,11 +11,11 @@
[app.common.pages.changes-builder :as pcb]
[app.common.pages.helpers :as cph]
[app.common.path.shapes-to-path :as stp]
[app.common.types.shape-tree :as ctt]
[app.common.uuid :as uuid]
[app.main.data.workspace.changes :as dch]
[app.main.data.workspace.selection :as dws]
[app.main.data.workspace.state-helpers :as wsh]
[app.util.names :as un]
[beicon.core :as rx]
[cuerdas.core :as str]
[potok.core :as ptk]))
@ -90,8 +90,8 @@
(let [page-id (:current-page-id state)
objects (wsh/lookup-page-objects state)
base-name (-> bool-type d/name str/capital (str "-1"))
name (-> (un/retrieve-used-names objects)
(un/generate-unique-name base-name))
name (-> (ctt/retrieve-used-names objects)
(ctt/generate-unique-name base-name))
shapes (selected-shapes state)]
(when-not (empty? shapes)

View file

@ -13,6 +13,7 @@
[app.common.pages.changes-spec :as pcs]
[app.common.pages.helpers :as cph]
[app.common.spec :as us]
[app.common.types.shape-tree :as ctst]
[app.common.uuid :as uuid]
[app.main.data.workspace.state-helpers :as wsh]
[app.main.data.workspace.undo :as dwu]
@ -165,7 +166,7 @@
(update-in state path (fn [file]
(-> file
(cp/process-changes redo-changes false)
(cph/update-object-indices page-id))))
(ctst/update-object-indices page-id))))
(catch :default err
(log/error :js/error err)
@ -191,6 +192,7 @@
process-page-changes
(fn [[page-id _changes]]
(update-indices page-id redo-changes))]
(rx/concat
(rx/from (map process-page-changes changes-by-pages))

View file

@ -108,4 +108,3 @@
:undo-changes []
:origin it
:save-undo? false})))))))))))

View file

@ -7,7 +7,7 @@
(ns app.main.data.workspace.drawing
"Drawing interactions."
(:require
[app.common.pages :as cp]
[app.common.types.shape :as cts]
[app.common.uuid :as uuid]
[app.main.data.workspace.common :as dwc]
[app.main.data.workspace.drawing.box :as box]
@ -91,7 +91,7 @@
(ptk/reify ::handle-drawing
ptk/UpdateEvent
(update [_ state]
(let [data (cp/make-minimal-shape type)]
(let [data (cts/make-minimal-shape type)]
(update-in state [:workspace-drawing :object] merge data)))
ptk/WatchEvent

View file

@ -9,8 +9,9 @@
[app.common.geom.point :as gpt]
[app.common.geom.shapes :as gsh]
[app.common.math :as mth]
[app.common.pages :as cp]
[app.common.pages.helpers :as cph]
[app.common.types.shape :as cts]
[app.common.types.shape-tree :as ctt]
[app.common.uuid :as uuid]
[app.main.data.workspace.drawing.common :as common]
[app.main.data.workspace.state-helpers :as wsh]
@ -65,11 +66,11 @@
focus (:workspace-focus-selected state)
zoom (get-in state [:workspace-local :zoom] 1)
fid (cph/frame-id-by-position objects initial)
fid (ctt/frame-id-by-position objects initial)
shape (get-in state [:workspace-drawing :object])
shape (-> shape
(cp/setup-shape {:x (:x initial)
(cts/setup-shape {:x (:x initial)
:y (:y initial)
:width 0.01
:height 0.01})

View file

@ -9,8 +9,8 @@
[app.common.geom.matrix :as gmt]
[app.common.geom.shapes :as gsh]
[app.common.math :as mth]
[app.common.pages :as cp]
[app.common.pages.helpers :as cph]
[app.common.types.shape :as cts]
[app.main.data.workspace.shapes :as dwsh]
[app.main.data.workspace.state-helpers :as wsh]
[app.main.data.workspace.undo :as dwu]
@ -55,7 +55,7 @@
(assoc :height 17 :width 4 :grow-type :auto-width)
click-draw?
(cp/setup-rect-selrect)
(cts/setup-rect-selrect)
:always
(-> (gsh/transform-shape)

View file

@ -8,7 +8,7 @@
(:require
[app.common.geom.shapes :as gsh]
[app.common.geom.shapes.path :as gsp]
[app.common.pages.helpers :as cph]
[app.common.types.shape-tree :as ctt]
[app.main.data.workspace.drawing.common :as common]
[app.main.data.workspace.state-helpers :as wsh]
[app.main.streams :as ms]
@ -47,7 +47,7 @@
(let [objects (wsh/lookup-page-objects state)
content (get-in state [:workspace-drawing :object :content] [])
position (get-in content [0 :params] nil)
frame-id (cph/frame-id-by-position objects position)]
frame-id (ctt/frame-id-by-position objects position)]
(-> state
(assoc-in [:workspace-drawing :object :frame-id] frame-id))))))

View file

@ -8,13 +8,13 @@
(:require
[app.common.data :as d]
[app.common.geom.shapes :as gsh]
[app.common.pages :as cp]
[app.common.pages.changes-builder :as pcb]
[app.common.pages.helpers :as cph]
[app.common.types.shape :as cts]
[app.common.types.shape-tree :as ctt]
[app.main.data.workspace.changes :as dch]
[app.main.data.workspace.selection :as dws]
[app.main.data.workspace.state-helpers :as wsh]
[app.util.names :as un]
[beicon.core :as rx]
[potok.core :as ptk]))
@ -71,12 +71,12 @@
(= (count shapes) 1)
(= (:type (first shapes)) :group))
(:name (first shapes))
(-> (un/retrieve-used-names objects)
(un/generate-unique-name base-name)))
(-> (ctt/retrieve-used-names objects)
(ctt/generate-unique-name base-name)))
selrect (gsh/selection-rect shapes)
group (-> (cp/make-minimal-group frame-id selrect gname)
(cp/setup-shape selrect)
group (-> (cts/make-minimal-group frame-id selrect gname)
(cts/setup-shape selrect)
(assoc :shapes (mapv :id shapes)
:parent-id parent-id
:frame-id frame-id

View file

@ -12,12 +12,12 @@
[app.common.pages.helpers :as cph]
[app.common.spec :as us]
[app.common.types.page :as ctp]
[app.common.types.shape-tree :as ctst]
[app.common.types.shape.interactions :as ctsi]
[app.common.uuid :as uuid]
[app.main.data.workspace.changes :as dch]
[app.main.data.workspace.state-helpers :as wsh]
[app.main.streams :as ms]
[app.util.names :as un]
[beicon.core :as rx]
[potok.core :as ptk]))
@ -32,7 +32,7 @@
flows (get-in page [:options :flows] [])
unames (into #{} (map :name flows))
name (un/generate-unique-name unames "Flow-1")
name (ctst/generate-unique-name unames "Flow-1")
new-flow {:id (uuid/next)
:name name
@ -182,7 +182,7 @@
from-frame-id (if (cph/frame-shape? from-shape)
from-id (:frame-id from-shape))
target-frame (cph/frame-by-position objects position)]
target-frame (ctst/frame-by-position objects position)]
(when (and (not= (:id target-frame) uuid/zero)
(not= (:id target-frame) from-frame-id)

View file

@ -10,12 +10,15 @@
[app.common.geom.point :as gpt]
[app.common.logging :as log]
[app.common.pages :as cp]
[app.common.pages.changes :as ch]
[app.common.pages.changes-builder :as pcb]
[app.common.pages.changes-spec :as pcs]
[app.common.pages.helpers :as cph]
[app.common.spec :as us]
[app.common.types.color :as ctc]
[app.common.types.container :as ctn]
[app.common.types.file :as ctf]
[app.common.types.shape-tree :as ctst]
[app.common.types.typography :as ctt]
[app.common.uuid :as uuid]
[app.main.data.dashboard :as dd]
@ -27,10 +30,11 @@
[app.main.data.workspace.selection :as dws]
[app.main.data.workspace.state-helpers :as wsh]
[app.main.data.workspace.undo :as dwu]
[app.main.features :as features]
[app.main.refs :as refs]
[app.main.repo :as rp]
[app.main.store :as st]
[app.util.i18n :refer [tr]]
[app.util.names :as un]
[app.util.router :as rt]
[app.util.time :as dt]
[beicon.core :as rx]
@ -137,7 +141,7 @@
(pcb/update-color color))]
(rx/of (dwu/start-undo-transaction)
(dch/commit-changes changes)
(sync-file (:current-file-id state) file-id)
(sync-file (:current-file-id state) file-id :colors (:id color))
(dwu/commit-undo-transaction))))
(defn update-color
@ -240,7 +244,7 @@
(pcb/update-typography typography))]
(rx/of (dwu/start-undo-transaction)
(dch/commit-changes changes)
(sync-file (:current-file-id state) file-id)
(sync-file (:current-file-id state) file-id :typographies (:id typography))
(dwu/commit-undo-transaction))))
(defn update-typography
@ -280,7 +284,7 @@
(defn- add-component2
"This is the second step of the component creation."
[selected]
[selected components-v2]
(ptk/reify ::add-component2
IDeref
(-deref [_] {:num-shapes (count selected)})
@ -293,7 +297,7 @@
shapes (dwg/shapes-for-grouping objects selected)]
(when-not (empty? shapes)
(let [[group _ changes]
(dwlh/generate-add-component it shapes objects page-id file-id)]
(dwlh/generate-add-component it shapes objects page-id file-id components-v2)]
(when-not (empty? (:redo-changes changes))
(rx/of (dch/commit-changes changes)
(dws/select-shapes (d/ordered-set (:id group)))))))))))
@ -307,10 +311,11 @@
(ptk/reify ::add-component
ptk/WatchEvent
(watch [_ state _]
(let [objects (wsh/lookup-page-objects state)
selected (->> (wsh/lookup-selected state)
(cph/clean-loops objects))]
(rx/of (add-component2 selected))))))
(let [objects (wsh/lookup-page-objects state)
selected (->> (wsh/lookup-selected state)
(cph/clean-loops objects))
components-v2 (features/active-feature? state :components-v2)]
(rx/of (add-component2 selected components-v2))))))
(defn rename-component
"Rename the component with the given id, in the current file library."
@ -352,18 +357,30 @@
component (cph/get-component libraries id)
all-components (-> state :workspace-data :components vals)
unames (into #{} (map :name) all-components)
new-name (un/generate-unique-name unames (:name component))
new-name (ctst/generate-unique-name unames (:name component))
[new-shape new-shapes _updated-shapes]
(dwlh/duplicate-component component)
components-v2 (features/active-feature? state :components-v2)
changes (-> (pcb/empty-changes it nil) ;; no objects are changed
(pcb/with-objects nil) ;; in the current page
(pcb/add-component (:id new-shape)
main-instance-page (when components-v2
(wsh/lookup-page state (:main-instance-page component)))
main-instance-shape (when components-v2
(ctn/get-shape main-instance-page (:main-instance-id component)))
[new-component-shape new-component-shapes
new-main-instance-shape new-main-instance-shapes]
(dwlh/duplicate-component component main-instance-page main-instance-shape)
changes (-> (pcb/empty-changes it nil)
(pcb/with-page main-instance-page)
(pcb/with-objects (:objects main-instance-page))
(pcb/add-objects new-main-instance-shapes {:ignore-touched true})
(pcb/add-component (:id new-component-shape)
(:path component)
new-name
new-shapes
[]))]
new-component-shapes
[]
(:id new-main-instance-shape)
(:id main-instance-page)))]
(rx/of (dch/commit-changes changes))))))
@ -521,7 +538,7 @@
libraries (wsh/get-libraries state)
container (cph/get-container local-file :page page-id)
shape (cph/get-shape container id)
shape (ctn/get-shape container id)
changes
(-> (pcb/empty-changes it)
@ -568,13 +585,15 @@
(ptk/reify ::update-component-sync
ptk/WatchEvent
(watch [_ state _]
(let [current-file-id (:current-file-id state)]
(let [current-file-id (:current-file-id state)
page (wsh/lookup-page state)
shape (ctn/get-shape page shape-id)]
(rx/of
(dwu/start-undo-transaction)
(update-component shape-id)
(sync-file current-file-id file-id)
(sync-file current-file-id file-id :components (:component-id shape))
(when (not= current-file-id file-id)
(sync-file file-id file-id))
(sync-file file-id file-id :components (:component-id shape)))
(dwu/commit-undo-transaction))))))
(defn update-component-in-bulk
@ -593,63 +612,83 @@
"Synchronize the given file from the given library. Walk through all
shapes in all pages in the file that use some color, typography or
component of the library, and copy the new values to the shapes. Do
it also for shapes inside components of the local file library."
[file-id library-id]
(us/assert ::us/uuid file-id)
(us/assert ::us/uuid library-id)
(ptk/reify ::sync-file
ptk/UpdateEvent
(update [_ state]
(if (not= library-id (:current-file-id state))
(d/assoc-in-when state [:workspace-libraries library-id :synced-at] (dt/now))
state))
it also for shapes inside components of the local file library.
ptk/WatchEvent
(watch [it state _]
(when (and (some? file-id) (some? library-id)) ; Prevent race conditions while navigating out of the file
(log/info :msg "SYNC-FILE"
:file (dwlh/pretty-file file-id state)
:library (dwlh/pretty-file library-id state))
(let [file (wsh/get-file state file-id)
If it's known that only one asset has changed, you can give its
type and id, and only shapes that use it will be synced, thus avoiding
a lot of unneeded checks."
([file-id library-id]
(sync-file file-id library-id nil nil))
([file-id library-id asset-type asset-id]
(us/assert ::us/uuid file-id)
(us/assert ::us/uuid library-id)
(us/assert (s/nilable #{:colors :components :typographies}) asset-type)
(us/assert (s/nilable ::us/uuid) asset-id)
(ptk/reify ::sync-file
ptk/UpdateEvent
(update [_ state]
(if (and (not= library-id (:current-file-id state))
(nil? asset-id))
(d/assoc-in-when state [:workspace-libraries library-id :synced-at] (dt/now))
state))
library-changes (reduce
pcb/concat-changes
(pcb/empty-changes it)
[(dwlh/generate-sync-library it file-id :components library-id state)
(dwlh/generate-sync-library it file-id :colors library-id state)
(dwlh/generate-sync-library it file-id :typographies library-id state)])
file-changes (reduce
pcb/concat-changes
(pcb/empty-changes it)
[(dwlh/generate-sync-file it file-id :components library-id state)
(dwlh/generate-sync-file it file-id :colors library-id state)
(dwlh/generate-sync-file it file-id :typographies library-id state)])
ptk/WatchEvent
(watch [it state _]
(when (and (some? file-id) (some? library-id)) ; Prevent race conditions while navigating out of the file
(log/info :msg "SYNC-FILE"
:file (dwlh/pretty-file file-id state)
:library (dwlh/pretty-file library-id state))
(let [file (wsh/get-file state file-id)
changes (pcb/concat-changes library-changes file-changes)]
sync-components? (or (nil? asset-type) (= asset-type :components))
sync-colors? (or (nil? asset-type) (= asset-type :colors))
sync-typographies? (or (nil? asset-type) (= asset-type :typographies))
(log/debug :msg "SYNC-FILE finished" :js/rchanges (log-changes
(:redo-changes changes)
file))
(rx/concat
(rx/of (dm/hide-tag :sync-dialog))
(when (seq (:redo-changes changes))
(rx/of (dch/commit-changes (assoc changes ;; TODO a ver qué pasa con esto
:file-id file-id))))
(when (not= file-id library-id)
;; When we have just updated the library file, give some time for the
;; update to finish, before marking this file as synced.
;; TODO: look for a more precise way of syncing this.
;; Maybe by using the stream (second argument passed to watch)
;; to wait for the corresponding changes-committed and then proceed
;; with the :update-sync mutation.
(rx/concat (rx/timer 3000)
(rp/mutation :update-sync
{:file-id file-id
:library-id library-id})))
(when (seq (:redo-changes library-changes))
(rx/of (sync-file-2nd-stage file-id library-id)))))))))
library-changes (reduce
pcb/concat-changes
(pcb/empty-changes it)
[(when sync-components?
(dwlh/generate-sync-library it file-id :components asset-id library-id state))
(when sync-colors?
(dwlh/generate-sync-library it file-id :colors asset-id library-id state))
(when sync-typographies?
(dwlh/generate-sync-library it file-id :typographies asset-id library-id state))])
file-changes (reduce
pcb/concat-changes
(pcb/empty-changes it)
[(when sync-components?
(dwlh/generate-sync-file it file-id :components asset-id library-id state))
(when sync-colors?
(dwlh/generate-sync-file it file-id :colors asset-id library-id state))
(when sync-typographies?
(dwlh/generate-sync-file it file-id :typographies asset-id library-id state))])
(defn sync-file-2nd-stage
changes (pcb/concat-changes library-changes file-changes)]
(log/debug :msg "SYNC-FILE finished" :js/rchanges (log-changes
(:redo-changes changes)
file))
(rx/concat
(rx/of (dm/hide-tag :sync-dialog))
(when (seq (:redo-changes changes))
(rx/of (dch/commit-changes (assoc changes ;; TODO a ver qué pasa con esto
:file-id file-id))))
(when (not= file-id library-id)
;; When we have just updated the library file, give some time for the
;; update to finish, before marking this file as synced.
;; TODO: look for a more precise way of syncing this.
;; Maybe by using the stream (second argument passed to watch)
;; to wait for the corresponding changes-committed and then proceed
;; with the :update-sync mutation.
(rx/concat (rx/timer 3000)
(rp/mutation :update-sync
{:file-id file-id
:library-id library-id})))
(when (and (seq (:redo-changes library-changes))
sync-components?)
(rx/of (sync-file-2nd-stage file-id library-id asset-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
@ -658,9 +697,10 @@
;; 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 library-id]
[file-id library-id asset-id]
(us/assert ::us/uuid file-id)
(us/assert ::us/uuid library-id)
(us/assert (s/nilable ::us/uuid) asset-id)
(ptk/reify ::sync-file-2nd-stage
ptk/WatchEvent
(watch [it state _]
@ -671,8 +711,8 @@
changes (reduce
pcb/concat-changes
(pcb/empty-changes it)
[(dwlh/generate-sync-file it file-id :components library-id state)
(dwlh/generate-sync-library it file-id :components library-id state)])]
[(dwlh/generate-sync-file it file-id :components asset-id library-id state)
(dwlh/generate-sync-library it file-id :components asset-id library-id state)])]
(when (seq (:redo-changes changes))
(log/debug :msg "SYNC-FILE (2nd stage) finished" :js/rchanges (log-changes
(:redo-changes changes)
@ -716,6 +756,48 @@
:callback do-dismiss}]
:sync-dialog))))))
(defn watch-component-changes
"Watch the state for changes that affect to any main instance. If a change is detected will throw
an update-component-sync, so changes are immediately propagated to the component and copies."
[]
(ptk/reify ::watch-component-changes
ptk/WatchEvent
(watch [_ state stream]
(let [components-v2 (features/active-feature? state :components-v2)
stopper
(->> stream
(rx/filter #(or (= :app.main.data.workspace/finalize-page (ptk/type %))
(= ::watch-component-changes (ptk/type %)))))
workspace-data-s
(->> (rx/concat
(rx/of nil)
(rx/from-atom refs/workspace-data {:emit-current-value? true})))
change-s
(->> stream
(rx/filter #(or (dch/commit-changes? %)
(= (ptk/type %) :app.main.data.workspace.notifications/handle-file-change)))
(rx/observe-on :async))
check-changes
(fn [[event data]]
(let [changes (-> event deref :changes)
components-changed (reduce #(into %1 (ch/components-changed data %2))
#{}
changes)]
(when (d/not-empty? components-changed)
(run! st/emit!
(map #(update-component-sync % (:id data))
components-changed)))))]
(when components-v2
(->> change-s
(rx/with-latest-from workspace-data-s)
(rx/map check-changes)
(rx/take-until stopper)))))))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Backend interactions
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
@ -762,12 +844,13 @@
[file-id library-id]
(ptk/reify ::attach-library
ptk/WatchEvent
(watch [_ _ _]
(let [fetched #(assoc-in %2 [:workspace-libraries (:id %1)] %1)
params {:file-id file-id
:library-id library-id}]
(watch [_ state _]
(let [components-v2 (features/active-feature? state :components-v2)
fetched #(assoc-in %2 [:workspace-libraries (:id %1)] %1)
params {:file-id file-id
:library-id library-id}]
(->> (rp/mutation :link-file-to-library params)
(rx/mapcat #(rp/query :file {:id library-id}))
(rx/mapcat #(rp/query :file {:id library-id :components-v2 components-v2}))
(rx/map #(partial fetched %)))))))
(defn unlink-file-from-library

View file

@ -16,9 +16,12 @@
[app.common.spec :as us]
[app.common.text :as txt]
[app.common.types.color :as ctc]
[app.common.types.component :as ctk]
[app.common.types.container :as ctn]
[app.common.types.shape-tree :as ctst]
[app.common.types.typography :as cty]
[app.main.data.workspace.groups :as dwg]
[app.main.data.workspace.state-helpers :as wsh]
[app.util.names :as un]
[cljs.spec.alpha :as s]
[clojure.set :as set]))
@ -55,51 +58,11 @@
;; ---- Components and instances creation ----
(defn make-component-shape
"Clone the shape and all children. Generate new ids and detach
from parent and frame. Update the original shapes to have links
to the new ones."
[shape objects file-id]
(assert (nil? (:component-id shape)))
(assert (nil? (:component-file shape)))
(assert (nil? (:shape-ref 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)
(dissoc :component-root?))
(nil? (:parent-id new-shape))
(dissoc :component-id
:component-file
:shape-ref)))
;; Make the original shape an instance of the new component.
;; If one of the original shape children already was a component
;; instance, maintain this instanceness untouched.
update-original-shape (fn [original-shape new-shape]
(cond-> original-shape
(nil? (:shape-ref original-shape))
(-> (assoc :shape-ref (:id new-shape))
(dissoc :touched))
(nil? (:parent-id new-shape))
(assoc :component-id (:id new-shape)
:component-file file-id
:component-root? true)
(some? (:parent-id new-shape))
(dissoc :component-root?)))]
(cph/clone-object shape nil objects update-new-shape update-original-shape)))
(defn generate-add-component
"If there is exactly one id, and it's a group, use it as root. Otherwise,
create a group that contains all ids. Then, make a component with it,
and link all shapes to their corresponding one in the component."
[it shapes objects page-id file-id]
[it shapes objects page-id file-id components-v2]
(if (and (= (count shapes) 1)
(:component-id (first shapes)))
[(first shapes) (pcb/empty-changes it)]
@ -114,73 +77,54 @@
(dwg/prepare-create-group it objects page-id shapes name true))
[new-shape new-shapes updated-shapes]
(make-component-shape group objects file-id)
(ctn/make-component-shape group objects file-id components-v2)
changes (-> changes
(pcb/add-component (:id new-shape)
path
name
new-shapes
updated-shapes))]
updated-shapes
(:id group)
page-id))]
[group new-shape changes])))
(defn duplicate-component
"Clone the root shape of the component and all children. Generate new
ids from all of them."
[component]
(let [component-root (cph/get-component-root component)]
(cph/clone-object component-root
nil
(get component :objects)
identity)))
[component main-instance-page main-instance-shape]
(let [position (gpt/add (gpt/point (:x main-instance-shape) (:y main-instance-shape))
(gpt/point (+ (:width main-instance-shape) 50) 0))
component-root (ctk/get-component-root component)
[new-component-shape new-component-shapes _]
(ctst/clone-object component-root
nil
(get component :objects)
identity)
[new-instance-shape new-instance-shapes]
(when (and (some? main-instance-page) (some? main-instance-shape))
(ctn/make-component-instance main-instance-page
{:id (:id new-component-shape)
:name (:name new-component-shape)
:objects (d/index-by :id new-component-shapes)}
(:component-file main-instance-shape)
position
false))]
[new-component-shape new-component-shapes
new-instance-shape new-instance-shapes]))
(defn generate-instantiate-component
"Generate changes to create a new instance from a component."
[it file-id component-id position page libraries]
(let [component (cph/get-component libraries file-id component-id)
component-shape (cph/get-shape component component-id)
orig-pos (gpt/point (:x component-shape) (:y component-shape))
delta (gpt/subtract position orig-pos)
objects (:objects page)
unames (volatile! (un/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 (un/generate-unique-name @unames (:name new-shape))]
(when (nil? (:parent-id original-shape))
(vswap! unames conj new-name))
(cond-> new-shape
true
(as-> $
(gsh/move $ delta)
(assoc $ :frame-id frame-id)
(assoc $ :parent-id
(or (:parent-id $) (:frame-id $)))
(dissoc $ :touched))
(nil? (:shape-ref original-shape))
(assoc :shape-ref (:id original-shape))
(nil? (:parent-id original-shape))
(assoc :component-id (:id original-shape)
:component-file file-id
:component-root? true
:name new-name)
(some? (:parent-id original-shape))
(dissoc :component-root?))))
[new-shape new-shapes _]
(cph/clone-object component-shape
nil
(get component :objects)
update-new-shape)
[new-shape new-shapes]
(ctn/make-component-instance page component file-id position false)
changes (reduce #(pcb/add-object %1 %2 {:ignore-touched true})
(pcb/empty-changes it (:id page))
@ -212,14 +156,19 @@
(defn generate-sync-file
"Generate changes to synchronize all shapes in all pages of the given file,
that use assets of the given type in the given library."
[it file-id asset-type library-id state]
that use assets of the given type in the given library.
If an asset id is given, only shapes linked to this particular asset will
be syncrhonized."
[it file-id asset-type asset-id library-id state]
(s/assert #{:colors :components :typographies} asset-type)
(s/assert (s/nilable ::us/uuid) asset-id)
(s/assert ::us/uuid file-id)
(s/assert ::us/uuid library-id)
(log/info :msg "Sync file with library"
:asset-type asset-type
:asset-id asset-id
:file (pretty-file file-id state)
:library (pretty-file library-id state))
@ -232,6 +181,7 @@
changes
(generate-sync-container it
asset-type
asset-id
library-id
state
(cph/make-container page :page))))
@ -240,11 +190,19 @@
(defn generate-sync-library
"Generate changes to synchronize all shapes in all components of the
local library of the given file, that use assets of the given type in
the given library."
[it file-id asset-type library-id state]
the given library.
If an asset id is given, only shapes linked to this particular asset will
be syncrhonized."
[it file-id asset-type asset-id library-id state]
(s/assert #{:colors :components :typographies} asset-type)
(s/assert (s/nilable ::us/uuid) asset-id)
(s/assert ::us/uuid file-id)
(s/assert ::us/uuid library-id)
(log/info :msg "Sync local components with library"
:asset-type asset-type
:asset-id asset-id
:file (pretty-file file-id state)
:library (pretty-file library-id state))
@ -257,6 +215,7 @@
changes
(generate-sync-container it
asset-type
asset-id
library-id
state
(cph/make-container local-component :component))))
@ -265,14 +224,14 @@
(defn- generate-sync-container
"Generate changes to synchronize all shapes in a particular container (a page
or a component) that use assets of the given type in the given library."
[it asset-type library-id state container]
[it asset-type asset-id library-id state container]
(if (cph/page? container)
(log/debug :msg "Sync page in local file" :page-id (:id container))
(log/debug :msg "Sync component in local library" :component-id (:id container)))
(let [linked-shapes (->> (vals (:objects container))
(filter #(uses-assets? asset-type % library-id (cph/page? container))))]
(let [linked-shapes (->> (vals (:objects container))
(filter #(uses-assets? asset-type asset-id % library-id (cph/page? container))))]
(loop [shapes (seq linked-shapes)
changes (-> (pcb/empty-changes it)
(pcb/with-container container)
@ -289,27 +248,26 @@
(defmulti uses-assets?
"Checks if a shape uses some asset of the given type in the given library."
(fn [asset-type _ _ _] asset-type))
(fn [asset-type _ _ _ _] asset-type))
(defmethod uses-assets? :components
[_ shape library-id page?]
(and (some? (:component-id shape))
(= (:component-file shape) library-id)
[_ component-id shape library-id page?]
(and (if (nil? component-id)
(ctk/uses-library-components? shape library-id)
(ctk/instance-of? shape library-id component-id))
(or (:component-root? shape) (not page?)))) ; avoid nested components inside pages
(defmethod uses-assets? :colors
[_ shape library-id _]
(ctc/uses-library-colors? shape library-id))
[_ color-id shape library-id _]
(if (nil? color-id)
(ctc/uses-library-colors? shape library-id)
(ctc/uses-library-color? shape library-id color-id)))
(defmethod uses-assets? :typographies
[_ shape library-id _]
(and (= (:type shape) :text)
(->> shape
:content
;; Check if any node in the content has a reference for the library
(txt/node-seq
#(and (some? (:typography-ref-id %))
(= (:typography-ref-file %) library-id))))))
[_ typography-id shape library-id _]
(if (nil? typography-id)
(cty/uses-library-typographies? shape library-id)
(cty/uses-library-typography? shape library-id typography-id)))
(defmulti generate-sync-shape
"Generate changes to synchronize one shape from all assets of the given type
@ -482,18 +440,18 @@
instance, and all its children, from the given component."
[changes libraries container shape-id reset?]
(log/debug :msg "Sync shape direct" :shape (str shape-id) :reset? reset?)
(let [shape-inst (cph/get-shape container shape-id)
(let [shape-inst (ctn/get-shape container shape-id)
component (cph/get-component libraries
(:component-file shape-inst)
(:component-id shape-inst))
shape-main (when component
(cph/get-shape component (:shape-ref shape-inst)))
(ctn/get-shape component (:shape-ref shape-inst)))
initial-root? (:component-root? shape-inst)
root-inst shape-inst
root-main (when component
(cph/get-component-root component))]
(ctk/get-component-root component))]
(if component
(generate-sync-shape-direct-recursive changes
@ -543,9 +501,9 @@
set-remote-synced?
(change-remote-synced shape-inst container true))
children-inst (mapv #(cph/get-shape container %)
children-inst (mapv #(ctn/get-shape container %)
(:shapes shape-inst))
children-main (mapv #(cph/get-shape component %)
children-main (mapv #(ctn/get-shape component %)
(:shapes shape-main))
only-inst (fn [changes child-inst]
@ -608,16 +566,16 @@
the values in the shape and all its children."
[changes libraries container shape-id]
(log/debug :msg "Sync shape inverse" :shape (str shape-id))
(let [shape-inst (cph/get-shape container shape-id)
(let [shape-inst (ctn/get-shape container shape-id)
component (cph/get-component libraries
(:component-file shape-inst)
(:component-id shape-inst))
shape-main (cph/get-shape component (:shape-ref shape-inst))
shape-main (ctn/get-shape component (:shape-ref shape-inst))
initial-root? (:component-root? shape-inst)
root-inst shape-inst
root-main (cph/get-component-root component)]
root-main (ctk/get-component-root component)]
(if component
(generate-sync-shape-inverse-recursive changes
@ -668,9 +626,9 @@
set-remote-synced?
(change-remote-synced shape-inst container true))
children-inst (mapv #(cph/get-shape container %)
children-inst (mapv #(ctn/get-shape container %)
(:shapes shape-inst))
children-main (mapv #(cph/get-shape component %)
children-main (mapv #(ctn/get-shape component %)
(:shapes shape-main))
only-inst (fn [changes child-inst]
@ -751,13 +709,13 @@
(reduce only-inst-cb changes children-inst)
:else
(if (cph/is-main-of? child-main child-inst)
(if (ctk/is-main-of? child-main child-inst)
(recur (next children-inst)
(next children-main)
(both-cb changes child-inst child-main))
(let [child-inst' (d/seek #(cph/is-main-of? child-main %) children-inst)
child-main' (d/seek #(cph/is-main-of? % child-inst) children-main)]
(let [child-inst' (d/seek #(ctk/is-main-of? child-main %) children-inst)
child-main' (d/seek #(ctk/is-main-of? % child-inst) children-main)]
(cond
(nil? child-inst')
(recur children-inst
@ -785,8 +743,8 @@
(defn- add-shape-to-instance
[changes component-shape index component container root-instance root-main omit-touched? set-remote-synced?]
(log/info :msg (str "ADD [P] " (:name component-shape)))
(let [component-parent-shape (cph/get-shape component (:parent-id component-shape))
parent-shape (d/seek #(cph/is-main-of? component-parent-shape %)
(let [component-parent-shape (ctn/get-shape component (:parent-id component-shape))
parent-shape (d/seek #(ctk/is-main-of? component-parent-shape %)
(cph/get-children-with-self (:objects container)
(:id root-instance)))
all-parents (into [(:id parent-shape)]
@ -811,7 +769,7 @@
original-shape)
[_ new-shapes _]
(cph/clone-object component-shape
(ctst/clone-object component-shape
(:id parent-shape)
(get component :objects)
update-new-shape
@ -853,8 +811,8 @@
(defn- add-shape-to-main
[changes shape index component page root-instance root-main]
(log/info :msg (str "ADD [C] " (:name shape)))
(let [parent-shape (cph/get-shape page (:parent-id shape))
component-parent-shape (d/seek #(cph/is-main-of? % parent-shape)
(let [parent-shape (ctn/get-shape page (:parent-id shape))
component-parent-shape (d/seek #(ctk/is-main-of? % parent-shape)
(cph/get-children-with-self (:objects component)
(:id root-main)))
all-parents (into [(:id component-parent-shape)]
@ -873,7 +831,7 @@
original-shape))
[_new-shape new-shapes updated-shapes]
(cph/clone-object shape
(ctst/clone-object shape
(:id component-parent-shape)
(get page :objects)
update-new-shape
@ -980,7 +938,7 @@
index-before
" -> "
index-after))
(let [parent (cph/get-shape container (:parent-id shape))
(let [parent (ctn/get-shape container (:parent-id shape))
changes' (-> changes
(update :redo-changes conj (make-change

View file

@ -8,10 +8,10 @@
(:require
[app.common.geom.point :as gpt]
[app.common.geom.shapes.path :as upg]
[app.common.pages.helpers :as cph]
[app.common.path.commands :as upc]
[app.common.path.shapes-to-path :as upsp]
[app.common.spec :as us]
[app.common.types.shape-tree :as ctt]
[app.main.data.workspace.changes :as dch]
[app.main.data.workspace.drawing.common :as dwdc]
[app.main.data.workspace.edition :as dwe]
@ -258,7 +258,7 @@
(let [objects (wsh/lookup-page-objects state)
content (get-in state [:workspace-drawing :object :content] [])
position (get-in content [0 :params] nil)
frame-id (cph/frame-id-by-position objects position)]
frame-id (ctt/frame-id-by-position objects position)]
(-> state
(assoc-in [:workspace-drawing :object :frame-id] frame-id))))))

View file

@ -16,12 +16,15 @@
[app.config :as cf]
[app.main.data.dashboard :as dd]
[app.main.data.fonts :as df]
[app.main.data.modal :as modal]
[app.main.data.workspace.changes :as dch]
[app.main.data.workspace.state-helpers :as wsh]
[app.main.data.workspace.thumbnails :as dwt]
[app.main.features :as features]
[app.main.repo :as rp]
[app.main.store :as st]
[app.util.http :as http]
[app.util.i18n :as i18n :refer [tr]]
[app.util.router :as rt]
[app.util.time :as dt]
[beicon.core :as rx]
@ -124,8 +127,7 @@
(rx/map persist-synchronous-changes)
(rx/take-until (rx/delay 100 stoper))
(rx/finalize (fn []
(log/debug :hint "finalize persistence: synchronous save loop"))))
)))))
(log/debug :hint "finalize persistence: synchronous save loop")))))))))
(defn persist-changes
[file-id changes]
@ -134,12 +136,14 @@
(ptk/reify ::persist-changes
ptk/WatchEvent
(watch [_ state _]
(let [sid (:session-id state)
file (get state :workspace-file)
params {:id (:id file)
:revn (:revn file)
:session-id sid
:changes-with-metadata (into [] changes)}]
(let [components-v2 (features/active-feature? state :components-v2)
sid (:session-id state)
file (get state :workspace-file)
params {:id (:id file)
:revn (:revn file)
:session-id sid
:changes-with-metadata (into [] changes)
:components-v2 components-v2}]
(when (= file-id (:id params))
(->> (rp/mutation :update-file params)
@ -175,13 +179,15 @@
(ptk/reify ::persist-synchronous-changes
ptk/WatchEvent
(watch [_ state _]
(let [sid (:session-id state)
(let [components-v2 (features/active-feature? state :components-v2)
sid (:session-id state)
file (get-in state [:workspace-libraries file-id])
params {:id (:id file)
:revn (:revn file)
:session-id sid
:changes changes}]
:changes changes
:components-v2 components-v2}]
(when (:id params)
(->> (rp/mutation :update-file params)
@ -261,8 +267,9 @@
(ptk/reify ::fetch-bundle
ptk/WatchEvent
(watch [_ state _]
(let [share-id (-> state :viewer-local :share-id)]
(->> (rx/zip (rp/query :file-raw {:id file-id})
(let [share-id (-> state :viewer-local :share-id)
components-v2 (features/active-feature? state :components-v2)]
(->> (rx/zip (rp/query :file-raw {:id file-id :components-v2 components-v2})
(rp/query :team-users {:file-id file-id})
(rp/query :project {:id project-id})
(rp/query :file-libraries {:file-id file-id})
@ -276,8 +283,16 @@
:file-comments-users file-comments-users}))
(rx/mapcat (fn [{:keys [project] :as bundle}]
(rx/of (ptk/data-event ::bundle-fetched bundle)
(df/load-team-fonts (:team-id project))))))))))
(df/load-team-fonts (:team-id project)))))
(rx/catch (fn [err]
(if (and (= (:type err) :restriction)
(= (:code err) :feature-disabled))
(let [team-id (:current-team-id state)]
(rx/of (modal/show
{:type :alert
:message (tr "errors.components-v2")
:on-accept #(st/emit! (rt/nav :dashboard-projects {:team-id team-id}))})))
(rx/throw err)))))))))
;; --- Helpers

View file

@ -15,6 +15,7 @@
[app.common.pages.helpers :as cph]
[app.common.spec :as us]
[app.common.types.page :as ctp]
[app.common.types.shape-tree :as ctt]
[app.common.types.shape.interactions :as ctsi]
[app.common.uuid :as uuid]
[app.main.data.modal :as md]
@ -26,7 +27,6 @@
[app.main.refs :as refs]
[app.main.streams :as ms]
[app.main.worker :as uw]
[app.util.names :as un]
[beicon.core :as rx]
[cljs.spec.alpha :as s]
[clojure.set :as set]
@ -284,7 +284,7 @@
move to the desired position, and recalculate parents and frames as needed."
[all-objects page ids delta it]
(let [shapes (map (d/getf all-objects) ids)
unames (volatile! (un/retrieve-used-names (:objects page)))
unames (volatile! (ctt/retrieve-used-names (:objects page)))
update-unames! (fn [new-name] (vswap! unames conj new-name))
all-ids (reduce #(into %1 (cons %2 (cph/get-children-ids all-objects %2))) (d/ordered-set) ids)
ids-map (into {} (map #(vector % (uuid/next))) all-ids)
@ -319,7 +319,7 @@
(defn- prepare-duplicate-frame-change
[changes objects page unames update-unames! ids-map obj delta]
(let [new-id (ids-map (:id obj))
frame-name (un/generate-unique-name @unames (:name obj))
frame-name (ctt/generate-unique-name @unames (:name obj))
_ (update-unames! frame-name)
new-frame (-> obj
@ -354,7 +354,7 @@
(if (some? obj)
(let [new-id (ids-map (:id obj))
parent-id (or parent-id frame-id)
name (un/generate-unique-name @unames (:name obj))
name (ctt/generate-unique-name @unames (:name obj))
_ (update-unames! name)
new-obj (-> obj
@ -395,7 +395,7 @@
(let [update-flows (fn [flows]
(reduce
(fn [flows frame]
(let [name (un/generate-unique-name @unames "Flow-1")
(let [name (ctt/generate-unique-name @unames "Flow-1")
_ (vswap! unames conj name)
new-flow {:id (uuid/next)
:name name

View file

@ -9,33 +9,33 @@
[app.common.data :as d]
[app.common.data.macros :as dm]
[app.common.geom.proportions :as gpr]
[app.common.pages :as cp]
[app.common.pages.changes-builder :as pcb]
[app.common.pages.helpers :as cph]
[app.common.spec :as us]
[app.common.types.page :as csp]
[app.common.types.shape :as spec.shape]
[app.common.types.shape.interactions :as csi]
[app.common.types.page :as ctp]
[app.common.types.shape :as cts]
[app.common.types.shape-tree :as ctst]
[app.common.types.shape.interactions :as ctsi]
[app.common.uuid :as uuid]
[app.main.data.workspace.changes :as dch]
[app.main.data.workspace.edition :as dwe]
[app.main.data.workspace.selection :as dws]
[app.main.data.workspace.shape-layout :as dwsl]
[app.main.data.workspace.state-helpers :as wsh]
[app.main.features :as features]
[app.main.streams :as ms]
[app.util.names :as un]
[beicon.core :as rx]
[cljs.spec.alpha :as s]
[potok.core :as ptk]))
(s/def ::shape-attrs ::spec.shape/shape-attrs)
(s/def ::shape-attrs ::cts/shape-attrs)
(defn get-shape-layer-position
[objects selected attrs]
;; Calculate the frame over which we're drawing
(let [position @ms/mouse-position
frame-id (:frame-id attrs (cph/frame-id-by-position objects position))
frame-id (:frame-id attrs (ctst/frame-id-by-position objects position))
shape (when-not (empty? selected)
(cph/get-base-shape objects selected))]
@ -52,8 +52,8 @@
(defn make-new-shape
[attrs objects selected]
(let [default-attrs (if (= :frame (:type attrs))
cp/default-frame-attrs
cp/default-shape-attrs)
cts/default-frame-attrs
cts/default-shape-attrs)
selected-non-frames
(into #{} (comp (map (d/getf objects))
@ -84,8 +84,8 @@
id (or (:id attrs) (uuid/next))
name (-> objects
(un/retrieve-used-names)
(un/generate-unique-name (:name attrs)))
(ctst/retrieve-used-names)
(ctst/generate-unique-name (:name attrs)))
shape (make-new-shape
(assoc attrs :id id :name name)
@ -117,7 +117,7 @@
to-move-shapes
(into []
(map (d/getf objects))
(reverse (cph/sort-z-index objects shapes)))
(reverse (ctst/sort-z-index objects shapes)))
changes
(when (d/not-empty? to-move-shapes)
@ -138,13 +138,17 @@
(ptk/reify ::delete-shapes
ptk/WatchEvent
(watch [it state _]
(let [page-id (:current-page-id state)
objects (wsh/lookup-page-objects state page-id)
page (wsh/lookup-page state page-id)
(let [file-id (:current-file-id state)
page-id (:current-page-id state)
file (wsh/get-file state file-id)
page (wsh/lookup-page state page-id)
objects (wsh/lookup-page-objects state page-id)
ids (cph/clean-loops objects ids)
lookup (d/getf objects)
components-v2 (features/active-feature? state :components-v2)
groups-to-unmask
(reduce (fn [group-ids id]
;; When the shape to delete is the mask of a masked group,
@ -164,7 +168,7 @@
;; If any of the deleted shapes is the destination of
;; some interaction, this must be deleted, too.
(let [interactions (:interactions shape)]
(some #(and (csi/has-destination %)
(some #(and (ctsi/has-destination %)
(contains? ids (:destination %)))
interactions)))
(vals objects))
@ -215,9 +219,22 @@
;; Any parent whose children are all deleted, must be deleted too.
(into (d/ordered-set) (find-all-empty-parents #{}))
components-to-delete
(if components-v2
(reduce (fn [components id]
(let [shape (get objects id)]
(if (and (= (:component-file shape) file-id) ;; Main instances should exist only in local file
(:main-instance? shape)) ;; but check anyway
(conj components (:component-id shape))
components)))
[]
(into ids all-children))
[])
changes (-> (pcb/empty-changes it page-id)
(pcb/with-page page)
(pcb/with-objects objects)
(pcb/with-library-data file)
(pcb/set-page-option :guides guides)
(pcb/remove-objects all-children)
(pcb/remove-objects ids)
@ -231,13 +248,18 @@
(d/update-when shape :interactions
(fn [interactions]
(into []
(remove #(and (csi/has-destination %)
(remove #(and (ctsi/has-destination %)
(contains? ids (:destination %))))
interactions)))))
(cond-> (seq starting-flows)
(pcb/update-page-option :flows (fn [flows]
(->> (map :id starting-flows)
(reduce csp/remove-flow flows))))))]
(reduce ctp/remove-flow flows))))))
changes (reduce (fn [changes component-id]
(pcb/delete-component changes component-id))
changes
components-to-delete)]
(rx/of (dch/commit-changes changes)
(dwsl/update-layout-positions all-parents))))))
@ -260,10 +282,10 @@
y (:y data (- vbc-y (/ height 2)))
page-id (:current-page-id state)
frame-id (-> (wsh/lookup-page-objects state page-id)
(cph/frame-id-by-position {:x frame-x :y frame-y}))
shape (-> (cp/make-minimal-shape type)
(ctst/frame-id-by-position {:x frame-x :y frame-y}))
shape (-> (cts/make-minimal-shape type)
(merge data)
(merge {:x x :y y})
(assoc :frame-id frame-id)
(cp/setup-rect-selrect))]
(cts/setup-rect-selrect))]
(rx/of (add-shape shape))))))

View file

@ -11,17 +11,16 @@
[app.common.geom.matrix :as gmt]
[app.common.geom.point :as gpt]
[app.common.geom.shapes :as gsh]
[app.common.pages :as cp]
[app.common.pages.changes-builder :as pcb]
[app.common.pages.helpers :as cph]
[app.common.spec :refer [max-safe-int min-safe-int]]
[app.common.types.shape :as cts]
[app.common.types.shape-tree :as ctt]
[app.common.uuid :as uuid]
[app.main.data.workspace.changes :as dch]
[app.main.data.workspace.selection :as dws]
[app.main.data.workspace.shapes :as dwsh]
[app.main.data.workspace.state-helpers :as wsh]
[app.util.color :as uc]
[app.util.names :as un]
[app.util.path.parser :as upp]
[app.util.svg :as usvg]
[beicon.core :as rx]
@ -183,7 +182,7 @@
(assoc :svg-attrs attrs)
(assoc :svg-viewbox (-> (select-keys svg-data [:width :height])
(assoc :x offset-x :y offset-y)))
(cp/setup-rect-selrect))))
(cts/setup-rect-selrect))))
(defn create-svg-root [frame-id svg-data]
(let [{:keys [name x y width height offset-x offset-y]} svg-data]
@ -195,7 +194,7 @@
:height height
:x (+ x offset-x)
:y (+ y offset-y)}
(cp/setup-rect-selrect)
(cts/setup-rect-selrect)
(assoc :svg-attrs (-> (:attrs svg-data)
(dissoc :viewBox :xmlns)
(d/without-keys usvg/inheritable-props))))))
@ -215,7 +214,7 @@
(assoc :svg-attrs (d/without-keys attrs usvg/inheritable-props))
(assoc :svg-viewbox (-> (select-keys svg-data [:width :height])
(assoc :x offset-x :y offset-y)))
(cp/setup-rect-selrect))))
(cts/setup-rect-selrect))))
(defn create-path-shape [name frame-id svg-data {:keys [attrs] :as data}]
(when (and (contains? attrs :d) (seq (:d attrs)))
@ -360,7 +359,7 @@
(let [{:keys [tag attrs hidden]} element-data
attrs (usvg/format-styles attrs)
element-data (cond-> element-data (map? element-data) (assoc :attrs attrs))
name (un/generate-unique-name unames (or (:id attrs) (tag->name tag)))
name (ctt/generate-unique-name unames (or (:id attrs) (tag->name tag)))
att-refs (usvg/find-attr-references attrs)
references (usvg/find-def-references (:defs svg-data) att-refs)
@ -437,17 +436,17 @@
(try
(let [page-id (:current-page-id state)
objects (wsh/lookup-page-objects state page-id)
frame-id (cph/frame-id-by-position objects position)
frame-id (ctt/frame-id-by-position objects position)
selected (wsh/lookup-selected state)
[vb-x vb-y vb-width vb-height] (svg-dimensions svg-data)
x (- x vb-x (/ vb-width 2))
y (- y vb-y (/ vb-height 2))
unames (un/retrieve-used-names objects)
unames (ctt/retrieve-used-names objects)
svg-name (->> (str/replace (:name svg-data) ".svg" "")
(un/generate-unique-name unames))
(ctt/generate-unique-name unames))
svg-data (-> svg-data
(assoc :x x

View file

@ -16,6 +16,7 @@
[app.common.pages.common :as cpc]
[app.common.pages.helpers :as cph]
[app.common.spec :as us]
[app.common.types.shape-tree :as ctt]
[app.main.data.workspace.changes :as dch]
[app.main.data.workspace.collapse :as dwc]
[app.main.data.workspace.guides :as dwg]
@ -752,7 +753,7 @@
(let [position @ms/mouse-position
page-id (:current-page-id state)
objects (wsh/lookup-page-objects state page-id)
frame-id (cph/frame-id-by-position objects position)
frame-id (ctt/frame-id-by-position objects position)
moving-shapes
(->> ids

View file

@ -4,10 +4,11 @@
;;
;; Copyright (c) UXBOX Labs SL
(ns app.main.ui.features
(ns app.main.features
(:require
[app.common.data :as d]
[app.common.logging :as log]
[app.config :as cfg]
[app.main.store :as st]
[okulary.core :as l]
[potok.core :as ptk]
@ -15,9 +16,9 @@
(log/set-level! :debug)
(def features-list #{:auto-layout})
(def features-list #{:auto-layout :components-v2})
(defn toggle-feature
(defn- toggle-feature
[feature]
(ptk/reify ::toggle-feature
ptk/UpdateEvent
@ -27,7 +28,6 @@
:result (if (not (contains? (:features state) feature))
"enabled"
"disabled"))
(-> state
(update :features
(fn [features]
@ -41,6 +41,13 @@
(assert (contains? features-list feature) "Not supported feature")
(st/emit! (toggle-feature feature)))
(defn active-feature?
([feature]
(active-feature? @st/state feature))
([state feature]
(assert (contains? features-list feature) "Not supported feature")
(contains? (get state :features) feature)))
(def features
(l/derived :features st/state))
@ -55,8 +62,14 @@
active-feature? (mf/deref active-feature-ref)]
active-feature?))
;; By default the features are active in local environments
(when *assert*
;; Activate all features in local environment
(doseq [f features-list]
(toggle-feature! f)))
;; Read initial enabled features from config, if set
(if-let [enabled-features @cfg/features]
(doseq [f enabled-features]
(toggle-feature! f))
(when *assert*
;; By default, all features disabled, except in development
;; environment, that are enabled except components-v2
(doseq [f features-list]
(when (not= f :components-v2)
(toggle-feature! f)))))

View file

@ -10,6 +10,7 @@
[app.common.data :as d]
[app.common.data.macros :as dm]
[app.common.pages.helpers :as cph]
[app.common.types.shape-tree :as ctt]
[app.main.data.workspace.state-helpers :as wsh]
[app.main.store :as st]
[okulary.core :as l]))
@ -284,7 +285,7 @@
(l/derived :options workspace-page))
(def workspace-frames
(l/derived cph/get-frames workspace-page-objects =))
(l/derived ctt/get-frames workspace-page-objects =))
(def workspace-editor
(l/derived :workspace-editor st/state))

View file

@ -21,6 +21,7 @@
[app.common.geom.shapes.bounds :as gsb]
[app.common.math :as mth]
[app.common.pages.helpers :as cph]
[app.common.types.shape-tree :as ctst]
[app.config :as cfg]
[app.main.fonts :as fonts]
[app.main.ui.context :as muc]
@ -61,7 +62,7 @@
(defn- calculate-dimensions
[objects]
(let [bounds
(->> (cph/get-root-objects objects)
(->> (ctst/get-root-objects objects)
(map (partial gsb/get-object-bounds objects))
(gsh/join-rects))]
(-> bounds

View file

@ -0,0 +1,78 @@
;; This Source Code Form is subject to the terms of the Mozilla Public
;; License, v. 2.0. If a copy of the MPL was not distributed with this
;; file, You can obtain one at http://mozilla.org/MPL/2.0/.
;;
;; Copyright (c) UXBOX Labs SL
(ns app.main.ui.alert
(:require
[app.main.data.modal :as modal]
[app.main.store :as st]
[app.main.ui.icons :as i]
[app.util.dom :as dom]
[app.util.i18n :as i18n :refer [tr t]]
[app.util.keyboard :as k]
[goog.events :as events]
[rumext.alpha :as mf])
(:import goog.events.EventType))
(mf/defc alert-dialog
{::mf/register modal/components
::mf/register-as :alert}
[{:keys [message
scd-message
title
on-accept
hint
accept-label
accept-style] :as props}]
(let [locale (mf/deref i18n/locale)
on-accept (or on-accept identity)
message (or message (t locale "ds.alert-title"))
accept-label (or accept-label (tr "ds.alert-ok"))
accept-style (or accept-style :danger)
title (or title (t locale "ds.alert-title"))
accept-fn
(mf/use-callback
(fn [event]
(dom/prevent-default event)
(st/emit! (modal/hide))
(on-accept props)))]
(mf/with-effect
(letfn [(on-keydown [event]
(when (k/enter? event)
(dom/prevent-default event)
(dom/stop-propagation event)
(st/emit! (modal/hide))
(on-accept props)))]
(->> (events/listen js/document EventType.KEYDOWN on-keydown)
(partial events/unlistenByKey))))
[:div.modal-overlay
[:div.modal-container.alert-dialog
[:div.modal-header
[:div.modal-header-title
[:h2 title]]
[:div.modal-close-button
{:on-click accept-fn} i/close]]
[:div.modal-content
(when (and (string? message) (not= message ""))
[:h3 message])
(when (and (string? scd-message) (not= scd-message ""))
[:h3 scd-message])
(when (string? hint)
[:p hint])]
[:div.modal-footer
[:div.action-buttons
[:input.accept-button
{:class (dom/classnames
:danger (= accept-style :danger)
:primary (= accept-style :primary))
:type "button"
:value accept-label
:on-click accept-fn}]]]]]))

View file

@ -11,7 +11,7 @@
(mf/defc element-icon
[{:keys [shape] :as props}]
[{:keys [shape main-instance?] :as props}]
(case (:type shape)
:frame i/artboard
:image i/image
@ -21,7 +21,9 @@
:rect i/box
:text i/text
:group (if (some? (:component-id shape))
i/component
(if main-instance?
i/component
i/component-copy)
(if (:masked-group? shape)
i/mask
i/folder))

View file

@ -21,6 +21,8 @@
(def current-project-id (mf/create-context nil))
(def current-page-id (mf/create-context nil))
(def current-file-id (mf/create-context nil))
(def libraries (mf/create-context nil))
(def scroll-ctx (mf/create-context nil))
(def active-frames-ctx (mf/create-context nil))
(def render-thumbnails (mf/create-context nil))
(def render-thumbnails (mf/create-context nil))
(def components-v2 (mf/create-context nil))

View file

@ -8,6 +8,7 @@
(:require
[app.common.data :as d]
[app.main.data.modal :as modal]
[app.main.features :as features]
[app.main.store :as st]
[app.main.ui.icons :as i]
[app.main.worker :as uw]
@ -56,6 +57,8 @@
:files (->> files (mapv #(assoc % :loading? true)))})
selected-option (mf/use-state :all)
components-v2 (features/use-feature :components-v2)
start-export
(fn []
(swap! state assoc :status :exporting)
@ -64,7 +67,7 @@
:team-id team-id
:export-type @selected-option
:files files
})
:components-v2 components-v2})
(rx/delay-emit 1000)
(rx/subs
(fn [msg]

View file

@ -92,19 +92,26 @@
on-delete
(fn [event]
(dom/stop-propagation event)
(if multi?
(st/emit! (modal/show
{:type :confirm
:title (tr "modals.delete-file-multi-confirm.title" file-count)
:message (tr "modals.delete-file-multi-confirm.message" file-count)
:accept-label (tr "modals.delete-file-multi-confirm.accept" file-count)
:on-accept delete-fn}))
(st/emit! (modal/show
{:type :confirm
:title (tr "modals.delete-file-confirm.title")
:message (tr "modals.delete-file-confirm.message")
:accept-label (tr "modals.delete-file-confirm.accept")
:on-accept delete-fn}))))
(if (:is-shared file)
(do (st/emit! (dd/fetch-library-using-files file))
(st/emit! (modal/show
{:type :delete-shared
:origin :delete
:on-accept delete-fn})))
(if multi?
(st/emit! (modal/show
{:type :confirm
:title (tr "modals.delete-file-multi-confirm.title" file-count)
:message (tr "modals.delete-file-multi-confirm.message" file-count)
:accept-label (tr "modals.delete-file-multi-confirm.accept" file-count)
:on-accept delete-fn}))
(st/emit! (modal/show
{:type :confirm
:title (tr "modals.delete-file-confirm.title")
:message (tr "modals.delete-file-confirm.message")
:accept-label (tr "modals.delete-file-confirm.accept")
:on-accept delete-fn})))))
on-move-success
(fn [team-id project-id]
@ -148,13 +155,10 @@
(fn [event]
(dom/prevent-default event)
(dom/stop-propagation event)
(st/emit! (dd/fetch-library-using-files file))
(st/emit! (modal/show
{:type :confirm
:message ""
:title (tr "modals.remove-shared-confirm.message" (:name file))
:hint (tr "modals.remove-shared-confirm.hint")
:cancel-label :omit
:accept-label (tr "modals.remove-shared-confirm.accept")
{:type :delete-shared
:origin :unpublish
:on-accept del-shared})))
on-export-files
@ -233,7 +237,7 @@
(when (or (seq current-projects) (seq other-teams))
[(tr "dashboard.move-to") nil sub-options "file-move-to"])
(if (:is-shared file)
[(tr "dashboard.remove-shared") on-del-shared nil "file-del-shared"]
[(tr "dashboard.unpublish-shared") on-del-shared nil "file-del-shared"]
[(tr "dashboard.add-shared") on-add-shared nil "file-add-shared"])
[:separator]
[(tr "dashboard.download-binary-file") on-export-binary-files nil "download-binary-file"]

View file

@ -127,5 +127,6 @@
[:section.dashboard-container
[:& grid {:project project
:files files
:on-create-clicked on-create-clicked}]]]))
:on-create-clicked on-create-clicked
:origin :files}]]]))

View file

@ -10,6 +10,7 @@
[app.common.math :as mth]
[app.main.data.dashboard :as dd]
[app.main.data.messages :as dm]
[app.main.features :as features]
[app.main.fonts :as fonts]
[app.main.refs :as refs]
[app.main.store :as st]
@ -36,9 +37,11 @@
(defn ask-for-thumbnail
"Creates some hooks to handle the files thumbnails cache"
[file]
(wrk/ask! {:cmd :thumbnails/generate
:revn (:revn file)
:file-id (:id file)}))
(let [components-v2 (features/active-feature? :components-v2)]
(wrk/ask! {:cmd :thumbnails/generate
:revn (:revn file)
:file-id (:id file)
:components-v2 components-v2})))
(mf/defc grid-item-thumbnail
{::mf/wrap [mf/memo]}
@ -72,12 +75,13 @@
(mf/defc grid-item
{:wrap [mf/memo]}
[{:keys [file navigate?] :as props}]
[{:keys [file navigate? origin] :as props}]
(let [file-id (:id file)
local (mf/use-state {:menu-open false
:menu-pos nil
:edition false})
selected-files (mf/deref refs/dashboard-selected-files)
dashboard-local (mf/deref refs/dashboard-local)
item-ref (mf/use-ref)
menu-ref (mf/use-ref)
selected? (contains? selected-files file-id)
@ -202,10 +206,12 @@
:top (:y (:menu-pos @local))
:navigate? navigate?
:on-edit on-edit
:on-menu-close on-menu-close}])]]]))
:on-menu-close on-menu-close
:origin origin
:dashboard-local dashboard-local}])]]]))
(mf/defc grid
[{:keys [files project on-create-clicked] :as props}]
[{:keys [files project on-create-clicked origin] :as props}]
(let [dragging? (mf/use-state false)
project-id (:id project)
@ -265,7 +271,8 @@
[:& grid-item
{:file item
:key (:id item)
:navigate? true}])]
:navigate? true
:origin origin}])]
:else
[:& empty-placeholder {:default? (:is-default project)
@ -273,7 +280,7 @@
:project project}])]))
(mf/defc line-grid-row
[{:keys [files selected-files on-load-more dragging?] :as props}]
[{:keys [files selected-files on-load-more dragging? origin] :as props}]
(let [rowref (mf/use-ref)
width (mf/use-state nil)
@ -319,7 +326,8 @@
:file item
:selected-files selected-files
:key (:id item)
:navigate? false}])
:navigate? false
:origin origin}])
(when (and (> limit 0)
(> (count files) limit))
[:div.grid-item.placeholder {:on-click on-load-more}
@ -328,7 +336,7 @@
(tr "dashboard.show-all-files")]])]))
(mf/defc line-grid
[{:keys [project team files on-load-more on-create-clicked] :as props}]
[{:keys [project team files on-load-more on-create-clicked origin] :as props}]
(let [dragging? (mf/use-state false)
project-id (:id project)
team-id (:id team)
@ -412,7 +420,8 @@
:team-id team-id
:selected-files selected-files
:on-load-more on-load-more
:dragging? @dragging?}]
:dragging? @dragging?
:origin origin}]
:else
[:& empty-placeholder {:dragging? @dragging?

View file

@ -42,5 +42,6 @@
[:h1 (tr "dashboard.libraries-title")]]]
[:section.dashboard-container
[:& grid {:files files
:project default-project}]]]))
:project default-project
:origin :libraries}]]]))

View file

@ -153,7 +153,8 @@
:team team
:on-load-more on-nav
:files files
:on-create-clicked (partial create-file "dashboard:empty-folder-placeholder")}]]))
:on-create-clicked (partial create-file "dashboard:empty-folder-placeholder")
:origin :project}]]))
(def recent-files-ref
(l/derived :dashboard-recent-files st/state))

View file

@ -0,0 +1,117 @@
;; This Source Code Form is subject to the terms of the Mozilla Public
;; License, v. 2.0. If a copy of the MPL was not distributed with this
;; file, You can obtain one at http://mozilla.org/MPL/2.0/.
;;
;; Copyright (c) UXBOX Labs SL
(ns app.main.ui.delete-shared
(:require
[app.main.data.dashboard :as dd]
[app.main.data.modal :as modal]
[app.main.refs :as refs]
[app.main.store :as st]
[app.main.ui.icons :as i]
[app.util.dom :as dom]
[app.util.i18n :as i18n :refer [tr]]
[app.util.keyboard :as k]
[goog.events :as events]
[rumext.alpha :as mf])
(:import goog.events.EventType))
(mf/defc delete-shared-dialog
{::mf/register modal/components
::mf/register-as :delete-shared}
[{:keys [on-accept
on-cancel
accept-style
origin] :as props}]
(let [on-accept (or on-accept identity)
on-cancel (or on-cancel identity)
cancel-label (tr "labels.cancel")
accept-style (or accept-style :danger)
is-delete? (= origin :delete)
dashboard-local (mf/deref refs/dashboard-local)
files->shared (:files-with-shared dashboard-local)
count-files (count (keys files->shared))
title (if is-delete?
(tr "modals.delete-shared-confirm.title")
(tr "modals.unpublish-shared-confirm.title"))
message (if is-delete?
(tr "modals.delete-shared-confirm.message")
(tr "modals.unpublish-shared-confirm.message"))
accept-label (if is-delete?
(tr "modals.delete-shared-confirm.accept")
(tr "modals.unpublish-shared-confirm.accept"))
scd-message (if is-delete?
(tr "modals.delete-shared-confirm.scd-message" (i18n/c count-files))
(tr "modals.unpublish-shared-confirm.scd-message" (i18n/c count-files)))
hint (if is-delete?
""
(tr "modals.unpublish-shared-confirm.hint" (i18n/c count-files)))
accept-fn
(mf/use-callback
(fn [event]
(dom/prevent-default event)
(st/emit! (modal/hide))
(on-accept props)))
cancel-fn
(mf/use-callback
(fn [event]
(dom/prevent-default event)
(st/emit! (modal/hide))
(on-cancel props)))]
(mf/with-effect
(letfn [(on-keydown [event]
(when (k/enter? event)
(dom/prevent-default event)
(dom/stop-propagation event)
(st/emit! (modal/hide))
(on-accept props)))]
(->> (events/listen js/document EventType.KEYDOWN on-keydown)
(partial events/unlistenByKey)))
#(st/emit! (dd/clean-temp-shared)))
[:div.modal-overlay
[:div.modal-container.confirm-dialog
[:div.modal-header
[:div.modal-header-title
[:h2 title]]
[:div.modal-close-button
{:on-click cancel-fn} i/close]]
[:div.modal-content.delete-shared
(when (and (string? message) (not= message ""))
[:h3 message])
(when (> (count files->shared) 0)
[:*
[:div
(when (and (string? scd-message) (not= scd-message ""))
[:h3 scd-message])
[:ul.file-list
(for [[id file] files->shared]
[:li.modal-item-element
{:key id}
[:span "- " (:name file)]])]]
(when (and (string? hint) (not= hint ""))
[:h3 hint])])]
[:div.modal-footer
[:div.action-buttons
(when-not (= cancel-label :omit)
[:input.cancel-button
{:type "button"
:value cancel-label
:on-click cancel-fn}])
[:input.accept-button
{:class (dom/classnames
:danger (= accept-style :danger)
:primary (= accept-style :primary))
:type "button"
:value accept-label
:on-click accept-fn}]]]]]))

View file

@ -53,6 +53,7 @@
(def close (icon-xref :close))
(def code (icon-xref :code))
(def component (icon-xref :component))
(def component-copy (icon-xref :component-copy))
(def copy (icon-xref :copy))
(def curve (icon-xref :curve))
(def cross (icon-xref :cross))

View file

@ -11,6 +11,7 @@
[app.main.data.messages :as msg]
[app.main.data.workspace :as dw]
[app.main.data.workspace.persistence :as dwp]
[app.main.features :as features]
[app.main.refs :as refs]
[app.main.store :as st]
[app.main.ui.context :as ctx]
@ -119,6 +120,8 @@
layout (mf/deref refs/workspace-layout)
wglobal (mf/deref refs/workspace-global)
components-v2 (features/use-feature :components-v2)
background-color (:background-color wglobal)]
;; Setting the layout preset by its name
@ -145,23 +148,22 @@
[:& (mf/provider ctx/current-team-id) {:value (:team-id project)}
[:& (mf/provider ctx/current-project-id) {:value (:id project)}
[:& (mf/provider ctx/current-page-id) {:value page-id}
[:section#workspace {:style {:background-color background-color}}
(when (not (:hide-ui layout))
[:& header {:file file
:page-id page-id
:project project
:layout layout}])
[:& context-menu]
(if (and (and file project)
(:initialized file))
[:& workspace-page {:key (dm/str "page-" page-id)
:page-id page-id
:file file
:wglobal wglobal
:layout layout}]
[:& workspace-loader])]]]]]))
[:& (mf/provider ctx/components-v2) {:value components-v2}
[:section#workspace {:style {:background-color background-color}}
(when (not (:hide-ui layout))
[:& header {:file file
:page-id page-id
:project project
:layout layout}])
[:& context-menu]
(if (and (and file project)
(:initialized file))
[:& workspace-page {:key (dm/str "page-" page-id)
:page-id page-id
:file file
:wglobal wglobal
:layout layout}]
[:& workspace-loader])]]]]]]))

View file

@ -188,7 +188,6 @@
(add-group % group-name)))))
(st/emit! (dwu/commit-undo-transaction)))
(defn- on-drop-asset
[event asset dragging? selected-assets selected-assets-full selected-assets-paths rename]
(let [create-typed-assets-group (partial create-assets-group rename)]
@ -568,7 +567,7 @@
(on-assets-delete)
(st/emit! (dwu/start-undo-transaction)
(dwl/delete-component {:id (:component-id @state)})
(dwl/sync-file file-id file-id)
(dwl/sync-file file-id file-id :components (:component-id @state))
(dwu/commit-undo-transaction)))))
on-rename
@ -1120,7 +1119,7 @@
(on-assets-delete)
(st/emit! (dwu/start-undo-transaction)
(dwl/delete-color color)
(dwl/sync-file file-id file-id)
(dwl/sync-file file-id file-id :color (:id color))
(dwu/commit-undo-transaction)))))
rename-color-clicked
@ -1762,7 +1761,7 @@
(on-assets-delete)
(st/emit! (dwu/start-undo-transaction)
(dwl/delete-typography (:id @state))
(dwl/sync-file file-id file-id)
(dwl/sync-file file-id file-id :typographies (:id @state))
(dwu/commit-undo-transaction)))))
editing-id (or (:rename-typography local-data)

View file

@ -15,6 +15,7 @@
[app.main.refs :as refs]
[app.main.store :as st]
[app.main.ui.components.shape-icon :as si]
[app.main.ui.context :as ctx]
[app.main.ui.hooks :as hooks]
[app.main.ui.icons :as i]
[app.util.dom :as dom]
@ -101,6 +102,11 @@
container? (or (cph/frame-shape? item)
(cph/group-shape? item))
components-v2 (mf/use-ctx ctx/components-v2)
main-instance? (if components-v2
(:main-instance? item)
true)
toggle-collapse
(mf/use-fn
(mf/deps expanded?)
@ -244,7 +250,8 @@
[:div {:on-double-click #(do (dom/stop-propagation %)
(dom/prevent-default %)
(st/emit! dw/zoom-to-selected-shape))}
[:& si/element-icon {:shape item}]]
[:& si/element-icon {:shape item
:main-instance? main-instance?}]]
[:& layer-name {:shape item
:name-ref ref
:on-start-edit #(reset! disable-drag true)
@ -444,7 +451,6 @@
(take (:num-items @filter-state))
filtered-objects-total))))
handle-show-more
(fn []
(when (<= (:num-items @filter-state) (count filtered-objects-total))
@ -542,7 +548,6 @@
(when last-hidden-frame
(dom/add-class! last-hidden-frame "sticky"))))]
[:div#layers.tool-window
(if (d/not-empty? focus)
[:div.tool-window-bar

View file

@ -6,22 +6,23 @@
(ns app.main.ui.workspace.sidebar.options.menus.component
(:require
[app.main.data.modal :as modal]
[app.main.data.workspace :as dw]
[app.main.data.workspace.libraries :as dwl]
[app.main.store :as st]
[app.main.ui.components.context-menu :refer [context-menu]]
[app.main.ui.context :as ctx]
[app.main.ui.icons :as i]
[app.util.dom :as dom]
[app.util.i18n :as i18n :refer [tr]]
[rumext.alpha :as mf]))
[app.main.data.modal :as modal]
[app.main.data.workspace :as dw]
[app.main.data.workspace.libraries :as dwl]
[app.main.store :as st]
[app.main.ui.components.context-menu :refer [context-menu]]
[app.main.ui.context :as ctx]
[app.main.ui.icons :as i]
[app.util.dom :as dom]
[app.util.i18n :as i18n :refer [tr]]
[rumext.alpha :as mf]))
(def component-attrs [:component-id :component-file :shape-ref])
(def component-attrs [:component-id :component-file :shape-ref :main-instance?])
(mf/defc component-menu
[{:keys [ids values shape-name] :as props}]
(let [current-file-id (mf/use-ctx ctx/current-file-id)
components-v2 (mf/use-ctx ctx/components-v2)
id (first ids)
local (mf/use-state {:menu-open false})
@ -29,6 +30,9 @@
component-id (:component-id values)
library-id (:component-file values)
show? (some? component-id)
main-instance? (if components-v2
(:main-instance? values)
true)
on-menu-click
(mf/use-callback
@ -69,7 +73,9 @@
[:span (tr "workspace.options.component")]]
[:div.element-set-content
[:div.row-flex.component-row
i/component
(if main-instance?
i/component
i/component-copy)
shape-name
[:div.row-actions
{:on-click on-menu-click}

View file

@ -10,6 +10,7 @@
[app.common.data.macros :as dm]
[app.common.pages.helpers :as cph]
[app.common.types.page :as ctp]
[app.common.types.shape-tree :as ctt]
[app.common.types.shape.interactions :as ctsi]
[app.common.uuid :as uuid]
[app.main.data.workspace :as dw]
@ -182,7 +183,7 @@
(let [objects (deref refs/workspace-page-objects)
destination (get objects (:destination interaction))
frames (mf/with-memo [objects] (cph/get-viewer-frames objects {:all-frames? (not= :navigate (:action-type interaction))}))
frames (mf/with-memo [objects] (ctt/get-viewer-frames objects {:all-frames? (not= :navigate (:action-type interaction))}))
overlay-pos-type (:overlay-pos-type interaction)
close-click-outside? (:close-click-outside interaction false)

View file

@ -6,8 +6,8 @@
(ns app.main.ui.workspace.sidebar.options.shapes.frame
(:require
[app.main.features :as features]
[app.main.refs :as refs]
[app.main.ui.features :as features]
[app.main.ui.workspace.sidebar.options.menus.blur :refer [blur-menu]]
[app.main.ui.workspace.sidebar.options.menus.constraints :refer [constraint-attrs constraints-menu]]
[app.main.ui.workspace.sidebar.options.menus.fill :refer [fill-attrs-shape fill-menu]]

View file

@ -9,7 +9,7 @@
[app.common.data :as d]
[app.common.geom.shapes :as gsh]
[app.common.math :as mth]
[app.common.pages.helpers :as cph]
[app.common.types.shape-tree :as ctst]
[app.common.uuid :as uuid]
[app.main.refs :as refs]
[app.util.geom.grid :as gg]
@ -134,7 +134,7 @@
[:g.grid-display {:style {:pointer-events "none"}}
(for [frame frames]
(when (and (not (is-transform? frame))
(not (cph/rotated-frame? frame))
(not (ctst/rotated-frame? frame))
(or (empty? focus) (contains? focus (:id frame))))
[:& grid-display-frame {:key (str "grid-" (:id frame))
:zoom zoom

View file

@ -11,6 +11,7 @@
[app.common.geom.shapes :as gsh]
[app.common.math :as mth]
[app.common.pages.helpers :as cph]
[app.common.types.shape-tree :as ctst]
[app.common.uuid :as uuid]
[app.main.data.workspace :as dw]
[app.main.refs :as refs]
@ -292,7 +293,7 @@
(when (or (nil? frame)
(and (cph/root-frame? frame)
(not (cph/rotated-frame? frame))))
(not (ctst/rotated-frame? frame))))
[:g.guide-area {:opacity (when frame-guide-outside? 0)}
(when-not disabled-guides?
(let [{:keys [x y width height]} (guide-area-axis pos vbox zoom frame axis)]

View file

@ -10,6 +10,7 @@
[app.common.geom.shapes :as gsh]
[app.common.pages :as cp]
[app.common.pages.helpers :as cph]
[app.common.types.shape-tree :as ctt]
[app.main.data.shortcuts :as dsc]
[app.main.data.workspace :as dw]
[app.main.data.workspace.path.shortcuts :as psc]
@ -183,7 +184,7 @@
ids (into
(d/ordered-set)
(cph/sort-z-index objects ids {:bottom-frames? mod?}))
(ctt/sort-z-index objects ids {:bottom-frames? mod?}))
grouped? (fn [id] (contains? #{:group :bool} (get-in objects [id :type])))
@ -218,7 +219,7 @@
(let [root-frame-ids
(mf/use-memo
(mf/deps objects)
#(cph/get-root-shapes-ids objects))
#(ctt/get-root-shapes-ids objects))
modifiers (select-keys modifiers root-frame-ids)]
(sfd/use-dynamic-modifiers objects globals/document modifiers)))
@ -229,7 +230,7 @@
(defn setup-active-frames
[objects hover-ids selected active-frames zoom transform vbox]
(let [all-frames (mf/use-memo (mf/deps objects) #(cph/get-root-frames-ids objects))
(let [all-frames (mf/use-memo (mf/deps objects) #(ctt/get-root-frames-ids objects))
selected-frames (mf/use-memo (mf/deps selected) #(->> all-frames (filter selected)))
xf-selected-frame (comp (remove cph/root-frame?)

View file

@ -11,7 +11,7 @@
[app.common.geom.matrix :as gmt]
[app.common.geom.point :as gpt]
[app.common.geom.shapes :as gsh]
[app.common.pages :as cp]
[app.common.types.shape :as cts]
[app.main.data.workspace :as dw]
[app.main.refs :as refs]
[app.main.store :as st]
@ -343,7 +343,7 @@
#(->> shapes
(map gsh/transform-shape)
(gsh/selection-rect)
(cp/setup-shape)))
(cts/setup-shape)))
on-resize
(fn [current-position _initial-position event]
(when (dom/left-mouse? event)
@ -371,7 +371,7 @@
#(->> shapes
(map gsh/transform-shape)
(gsh/selection-rect)
(cp/setup-shape)))]
(cts/setup-shape)))]
[:& controls-selection
{:shape shape

View file

@ -10,7 +10,7 @@
[app.common.data.macros :as dm]
[app.common.geom.point :as gpt]
[app.common.geom.shapes :as gsh]
[app.common.pages.helpers :as cph]
[app.common.types.shape-tree :as ctt]
[app.common.uuid :as uuid]
[app.main.data.workspace :as dw]
[app.main.data.workspace.interactions :as dwi]
@ -178,7 +178,7 @@
on-frame-enter (unchecked-get props "on-frame-enter")
on-frame-leave (unchecked-get props "on-frame-leave")
on-frame-select (unchecked-get props "on-frame-select")
frames (cph/get-frames objects)]
frames (ctt/get-frames objects)]
[:g.frame-titles
(for [frame frames]

View file

@ -13,6 +13,7 @@
[app.common.uri :as u]
[app.config :as cf]
[app.main.data.fonts :as df]
[app.main.features :as features]
[app.main.render :as render]
[app.main.repo :as repo]
[app.main.store :as st]
@ -99,22 +100,24 @@
(mf/defc object-svg
[{:keys [page-id file-id object-id render-embed?]}]
(let [fetch-state (mf/use-fn
(mf/deps file-id page-id object-id)
(fn []
(->> (rx/zip
(repo/query! :font-variants {:file-id file-id})
(repo/query! :page {:file-id file-id
:page-id page-id
:object-id object-id}))
(rx/tap (fn [[fonts]]
(when (seq fonts)
(st/emit! (df/fonts-fetched fonts)))))
(rx/map (comp :objects second))
(rx/map (fn [objects]
(let [objects (render/adapt-objects-for-shape objects object-id)]
{:objects objects
:object (get objects object-id)}))))))
(let [components-v2 (features/use-feature :components-v2)
fetch-state (mf/use-fn
(mf/deps file-id page-id object-id)
(fn []
(->> (rx/zip
(repo/query! :font-variants {:file-id file-id})
(repo/query! :page {:file-id file-id
:page-id page-id
:object-id object-id
:components-v2 components-v2}))
(rx/tap (fn [[fonts]]
(when (seq fonts)
(st/emit! (df/fonts-fetched fonts)))))
(rx/map (comp :objects second))
(rx/map (fn [objects]
(let [objects (render/adapt-objects-for-shape objects object-id)]
{:objects objects
:object (get objects object-id)}))))))
{:keys [objects object]} (use-resource fetch-state)]
@ -124,8 +127,8 @@
(when object
(dom/set-page-style!
{:size (str/concat
(mth/ceil (:width object)) "px "
(mth/ceil (:height object)) "px")})))
(mth/ceil (:width object)) "px "
(mth/ceil (:height object)) "px")})))
(when objects
[:& render/object-svg
@ -135,17 +138,19 @@
(mf/defc objects-svg
[{:keys [page-id file-id object-ids render-embed?]}]
(let [fetch-state (mf/use-fn
(mf/deps file-id page-id)
(fn []
(->> (rx/zip
(repo/query! :font-variants {:file-id file-id})
(repo/query! :page {:file-id file-id
:page-id page-id}))
(rx/tap (fn [[fonts]]
(when (seq fonts)
(st/emit! (df/fonts-fetched fonts)))))
(rx/map (comp :objects second)))))
(let [components-v2 (features/use-feature :components-v2)
fetch-state (mf/use-fn
(mf/deps file-id page-id)
(fn []
(->> (rx/zip
(repo/query! :font-variants {:file-id file-id})
(repo/query! :page {:file-id file-id
:page-id page-id
:components-v2 components-v2}))
(rx/tap (fn [[fonts]]
(when (seq fonts)
(st/emit! (df/fonts-fetched fonts)))))
(rx/map (comp :objects second)))))
objects (use-resource fetch-state)]

View file

@ -8,7 +8,8 @@
(:require
[app.common.geom.point :as gpt]
[app.common.geom.shapes :as gsh]
[app.common.pages.helpers :as cph]))
[app.common.pages.helpers :as cph]
[app.common.types.shape-tree :as ctst]))
(defn selrect-snap-points [{:keys [x y width height] :as selrect}]
#{(gpt/point x y)
@ -38,7 +39,7 @@
(cond
(and (some? frame)
(not (cph/rotated-frame? frame))
(not (ctst/rotated-frame? frame))
(not (cph/root-frame? frame)))
#{}

View file

@ -1,38 +0,0 @@
;; This Source Code Form is subject to the terms of the Mozilla Public
;; License, v. 2.0. If a copy of the MPL was not distributed with this
;; file, You can obtain one at http://mozilla.org/MPL/2.0/.
;;
;; Copyright (c) UXBOX Labs SL
(ns app.util.names
(:require
[app.common.data :as d]
[app.common.spec :as us]
[cljs.spec.alpha :as s]))
(s/def ::set-of-string (s/every string? :kind set?))
(defn- extract-numeric-suffix
[basename]
(if-let [[_ p1 p2] (re-find #"(.*)-([0-9]+)$" basename)]
[p1 (+ 1 (d/parse-integer p2))]
[basename 1]))
(defn retrieve-used-names
[objects]
(into #{} (comp (map :name) (remove nil?)) (vals objects)))
(defn generate-unique-name
"A unique name generator"
[used basename]
(s/assert ::set-of-string used)
(s/assert ::us/string basename)
(if-not (contains? used basename)
basename
(let [[prefix initial] (extract-numeric-suffix basename)]
(loop [counter initial]
(let [candidate (str prefix "-" counter)]
(if (contains? used candidate)
(recur (inc counter))
candidate))))))

View file

@ -12,6 +12,7 @@
[app.common.data :as d]
[app.common.pages.diff :as diff]
[app.common.pages.helpers :as cph]
[app.common.types.shape-tree :as ctst]
[app.common.uuid :as uuid]
[app.util.geom.grid :as gg]
[app.util.geom.snap-points :as snap]
@ -55,7 +56,7 @@
(defn get-grids-snap-points
[frame coord]
(if (not (cph/rotated-frame? frame))
(if (not (ctst/rotated-frame? frame))
[]
(let [grid->snap (fn [[grid-type position]]
{:type :layout
@ -196,7 +197,7 @@
(defn add-page
"Adds page information"
[snap-data {:keys [objects options] :as page}]
(let [frames (cph/get-frames objects)
(let [frames (ctst/get-frames objects)
shapes (->> (vals (:objects page))
(remove cph/frame-shape?))
guides (vals (:guides options))

View file

@ -149,8 +149,8 @@
(->> (r/render-components (:data file))
(rx/map #(vector (str (:id file) "/components.svg") %))))
(defn fetch-file-with-libraries [file-id]
(->> (rx/zip (rp/query :file {:id file-id})
(defn fetch-file-with-libraries [file-id components-v2]
(->> (rx/zip (rp/query :file {:id file-id :components-v2 components-v2})
(rp/query :file-libraries {:file-id file-id}))
(rx/map
(fn [[file file-libraries]]
@ -351,7 +351,7 @@
(update file-id dissoc :libraries))))
(defn collect-files
[file-id export-type]
[file-id export-type components-v2]
(letfn [(fetch-dependencies [[files pending]]
(if (empty? pending)
@ -365,7 +365,7 @@
;; The file is already in the result
(rx/of [files pending])
(->> (fetch-file-with-libraries next)
(->> (fetch-file-with-libraries next components-v2)
(rx/map
(fn [file]
[(-> files
@ -381,9 +381,9 @@
(rx/map #(process-export file-id export-type %))))))
(defn export-file
[team-id file-id export-type]
[team-id file-id export-type components-v2]
(let [files-stream (->> (collect-files file-id export-type)
(let [files-stream (->> (collect-files file-id export-type components-v2)
(rx/share))
manifest-stream
@ -471,12 +471,12 @@
:file-id (:id file)}))))))))
(defmethod impl/handler :export-standard-file
[{:keys [team-id files export-type] :as message}]
[{:keys [team-id files export-type components-v2] :as message}]
(->> (rx/from files)
(rx/mapcat
(fn [file]
(->> (export-file team-id (:id file) export-type)
(->> (export-file team-id (:id file) export-type components-v2)
(rx/map
(fn [value]
(if (contains? value :type)

View file

@ -14,8 +14,8 @@
[app.common.geom.shapes.path :as gpa]
[app.common.logging :as log]
[app.common.media :as cm]
[app.common.pages :as cp]
[app.common.text :as ct]
[app.common.types.file :as ctf]
[app.common.uuid :as uuid]
[app.main.repo :as rp]
[app.util.http :as http]
@ -133,7 +133,7 @@
:name (:name context)
:is-shared (:shared context)
:project-id (:project-id context)
:data (-> cp/empty-file-data (assoc :id file-id))})))
:data (-> ctf/empty-file-data (assoc :id file-id))})))
(defn link-file-libraries
"Create a new file on the back-end"

View file

@ -48,11 +48,12 @@
(= :request-body-too-large code)))
(defn- request-data-for-thumbnail
[file-id revn]
[file-id revn components-v2]
(let [path "api/rpc/query/file-data-for-thumbnail"
params {:file-id file-id
:revn revn
:strip-frames-with-thumbnails true}
:strip-frames-with-thumbnails true
:components-v2 components-v2}
request {:method :get
:uri (u/join (cfg/get-public-uri) path)
:credentials "include"
@ -107,18 +108,18 @@
(rx/map (constantly params)))))
(defmethod impl/handler :thumbnails/generate
[{:keys [file-id revn] :as message}]
[{:keys [file-id revn components-v2] :as message}]
(letfn [(on-result [{:keys [data props]}]
{:data data
:fonts (:fonts props)})
(on-cache-miss [_]
(->> (request-data-for-thumbnail file-id revn)
(->> (request-data-for-thumbnail file-id revn components-v2)
(rx/map render-thumbnail)
(rx/mapcat persist-thumbnail)))]
(if (debug? :disable-thumbnail-cache)
(->> (request-data-for-thumbnail file-id revn)
(->> (request-data-for-thumbnail file-id revn components-v2)
(rx/map render-thumbnail))
(->> (request-thumbnail file-id revn)
(rx/catch not-found? on-cache-miss)

View file

@ -8,8 +8,8 @@
(:require
[app.common.data :as d]
[app.common.logging :as l]
[app.common.pages.helpers :as cph]
[app.common.transit :as t]
[app.common.types.file :as ctf]
[app.common.uuid :as uuid]
[app.main.data.dashboard.shortcuts]
[app.main.data.viewer.shortcuts]
@ -211,73 +211,9 @@
([state show-ids] (dump-tree' state show-ids false))
([state 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])
libraries (get state :workspace-libraries)
root (d/seek #(nil? (:parent-id %)) (vals objects))]
(letfn [(show-shape [shape-id level objects]
(let [shape (get objects shape-id)]
(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))
(when show-touched
(when (seq (:touched shape))
(println (str (str/repeat " " level)
" "
(str (:touched shape)))))
(when (:remote-synced? shape)
(println (str (str/repeat " " level)
" (remote-synced)"))))
(when (:shapes shape)
(dorun (for [shape-id (:shapes shape)]
(show-shape shape-id (inc level) objects))))))
(show-component [shape objects]
(if (nil? (:shape-ref shape))
""
(let [root-shape (cph/get-component-shape objects shape)
component-id (when root-shape (:component-id root-shape))
component-file-id (when root-shape (:component-file root-shape))
component-file (when component-file-id (get libraries component-file-id nil))
component (when component-id
(if component-file
(get-in component-file [:data :components component-id])
(get components component-id)))
component-shape (when (and component (:shape-ref shape))
(get-in component [:objects (:shape-ref shape)]))]
(str/format " %s--> %s%s%s"
(cond (:component-root? shape) "#"
(:component-id shape) "@"
:else "-")
(when component-file (str/format "<%s> " (:name component-file)))
(or (:name component-shape) "?")
(if (or (:component-root? shape)
(nil? (:component-id shape))
true)
""
(let [component-id (:component-id shape)
component-file-id (:component-file shape)
component-file (when component-file-id (get libraries component-file-id nil))
component (if component-file
(get-in component-file [:data :components component-id])
(get components component-id))]
(str/format " (%s%s)"
(when component-file (str/format "<%s> " (:name component-file)))
(:name component))))))))]
(println "[Page]")
(show-shape (:id root) 0 objects)
(dorun (for [component (vals components)]
(do
(println)
(println (str/format "[%s]" (:name component)))
(show-shape (:id component) 0 (:objects component)))))))))
file-data (get state :workspace-data)
libraries (get state :workspace-libraries)]
(ctf/dump-tree file-data page-id libraries show-ids show-touched))))
(defn ^:export dump-tree
([] (dump-tree' @st/state))

View file

@ -7,8 +7,11 @@
;; This namespace is only to export the functions for toggle features
(ns features
(:require
[app.main.ui.features :as features]))
[app.main.features :as features]))
(defn ^:export autolayout []
(features/toggle-feature! :auto-layout))
(defn ^:export components-v2 []
(features/toggle-feature! :components-v2))