mirror of
https://github.com/penpot/penpot.git
synced 2025-06-10 21:11:37 +02:00
commit
0f5ce3b836
24 changed files with 1133 additions and 235 deletions
|
@ -182,6 +182,13 @@
|
||||||
(assoc m key (apply f found args))
|
(assoc m key (apply f found args))
|
||||||
m)))
|
m)))
|
||||||
|
|
||||||
|
(defn assoc-when
|
||||||
|
[m key v]
|
||||||
|
(let [found (get m key sentinel)]
|
||||||
|
(if-not (identical? sentinel found)
|
||||||
|
(assoc m key v)
|
||||||
|
m)))
|
||||||
|
|
||||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||||
;; Data Parsing / Conversion
|
;; Data Parsing / Conversion
|
||||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||||
|
|
|
@ -44,6 +44,9 @@
|
||||||
(integer? %)
|
(integer? %)
|
||||||
(>= % min-safe-int)
|
(>= % min-safe-int)
|
||||||
(<= % max-safe-int)))
|
(<= % max-safe-int)))
|
||||||
|
(s/def ::component-id uuid?)
|
||||||
|
(s/def ::component-file uuid?)
|
||||||
|
(s/def ::shape-ref uuid?)
|
||||||
|
|
||||||
(s/def ::safe-number
|
(s/def ::safe-number
|
||||||
#(and
|
#(and
|
||||||
|
@ -216,7 +219,10 @@
|
||||||
|
|
||||||
(s/def ::shape
|
(s/def ::shape
|
||||||
(s/and ::minimal-shape ::shape-attrs
|
(s/and ::minimal-shape ::shape-attrs
|
||||||
(s/keys :opt-un [::id])))
|
(s/keys :opt-un [::id
|
||||||
|
::component-id
|
||||||
|
::component-file
|
||||||
|
::shape-ref])))
|
||||||
|
|
||||||
(s/def :internal.page/objects (s/map-of uuid? ::shape))
|
(s/def :internal.page/objects (s/map-of uuid? ::shape))
|
||||||
|
|
||||||
|
@ -356,6 +362,18 @@
|
||||||
(defmethod change-spec :del-media [_]
|
(defmethod change-spec :del-media [_]
|
||||||
(s/keys :req-un [::id]))
|
(s/keys :req-un [::id]))
|
||||||
|
|
||||||
|
(s/def :internal.changes.add-component/shapes
|
||||||
|
(s/coll-of ::shape))
|
||||||
|
|
||||||
|
(defmethod change-spec :add-component [_]
|
||||||
|
(s/keys :req-un [::id ::name :internal.changes.add-component/shapes]))
|
||||||
|
|
||||||
|
(defmethod change-spec :del-component [_]
|
||||||
|
(s/keys :req-un [::id]))
|
||||||
|
|
||||||
|
(defmethod change-spec :update-component [_]
|
||||||
|
(s/keys :req-un [::id ::name :internal.changes.add-component/shapes]))
|
||||||
|
|
||||||
(s/def ::change (s/multi-spec change-spec :type))
|
(s/def ::change (s/multi-spec change-spec :type))
|
||||||
(s/def ::changes (s/coll-of ::change))
|
(s/def ::changes (s/coll-of ::change))
|
||||||
|
|
||||||
|
@ -473,6 +491,18 @@
|
||||||
:points []
|
:points []
|
||||||
:segments [])))
|
:segments [])))
|
||||||
|
|
||||||
|
(defn make-minimal-group
|
||||||
|
[frame-id selection-rect group-name]
|
||||||
|
{:id (uuid/next)
|
||||||
|
:type :group
|
||||||
|
:name group-name
|
||||||
|
:shapes []
|
||||||
|
:frame-id frame-id
|
||||||
|
:x (:x selection-rect)
|
||||||
|
:y (:y selection-rect)
|
||||||
|
:width (:width selection-rect)
|
||||||
|
:height (:height selection-rect)})
|
||||||
|
|
||||||
(defn make-file-data
|
(defn make-file-data
|
||||||
([] (make-file-data (uuid/next)))
|
([] (make-file-data (uuid/next)))
|
||||||
([id]
|
([id]
|
||||||
|
@ -745,6 +775,24 @@
|
||||||
[data {:keys [id]}]
|
[data {:keys [id]}]
|
||||||
(update data :media dissoc id))
|
(update data :media dissoc id))
|
||||||
|
|
||||||
|
(defmethod process-change :add-component
|
||||||
|
[data {:keys [id name shapes]}]
|
||||||
|
(assoc-in data [:components id]
|
||||||
|
{:id id
|
||||||
|
:name name
|
||||||
|
:objects (d/index-by :id shapes)}))
|
||||||
|
|
||||||
|
(defmethod process-change :del-component
|
||||||
|
[data {:keys [id]}]
|
||||||
|
(d/dissoc-in data [:components id]))
|
||||||
|
|
||||||
|
(defmethod process-change :update-component
|
||||||
|
[data {:keys [id name shapes]}]
|
||||||
|
(update-in data [:components id]
|
||||||
|
#(assoc %
|
||||||
|
:name name
|
||||||
|
:objects (d/index-by :id shapes))))
|
||||||
|
|
||||||
(defmethod process-operation :set
|
(defmethod process-operation :set
|
||||||
[shape op]
|
[shape op]
|
||||||
(let [attr (:attr op)
|
(let [attr (:attr op)
|
||||||
|
|
|
@ -12,14 +12,53 @@
|
||||||
[app.common.data :as d]
|
[app.common.data :as d]
|
||||||
[app.common.uuid :as uuid]))
|
[app.common.uuid :as uuid]))
|
||||||
|
|
||||||
|
(defn walk-pages
|
||||||
|
"Go through all pages of a file and apply a function to each one"
|
||||||
|
;; The function receives two parameters (page-id and page), and
|
||||||
|
;; returns the updated page.
|
||||||
|
[f data]
|
||||||
|
(update data :pages-index #(d/mapm f %)))
|
||||||
|
|
||||||
|
(defn select-objects
|
||||||
|
"Get a list of all objects in a page that satisfy a condition"
|
||||||
|
[f page]
|
||||||
|
(filter f (vals (get page :objects))))
|
||||||
|
|
||||||
|
(defn update-object-list
|
||||||
|
"Update multiple objects in a page at once"
|
||||||
|
[page objects-list]
|
||||||
|
(update page :objects
|
||||||
|
#(into % (d/index-by :id objects-list))))
|
||||||
|
|
||||||
|
(defn get-root-component
|
||||||
|
"Get the root shape linked to the component for this shape, if any"
|
||||||
|
[id objects]
|
||||||
|
(let [obj (get objects id)]
|
||||||
|
(if-let [component-id (:component-id obj)]
|
||||||
|
id
|
||||||
|
(if-let [parent-id (:parent-id obj)]
|
||||||
|
(get-root-component parent-id obj)
|
||||||
|
nil))))
|
||||||
|
|
||||||
(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]
|
||||||
(let [shapes (get-in objects [id :shapes])]
|
;; TODO: find why does this sometimes come as a list instead of vector
|
||||||
|
(let [shapes (vec (get-in objects [id :shapes]))]
|
||||||
(if shapes
|
(if shapes
|
||||||
(d/concat shapes (mapcat #(get-children % objects) shapes))
|
(d/concat shapes (mapcat #(get-children % objects) shapes))
|
||||||
[])))
|
[])))
|
||||||
|
|
||||||
|
(defn get-children-objects
|
||||||
|
"Retrieve all children objects recursively for a given object"
|
||||||
|
[id objects]
|
||||||
|
(map #(get objects %) (get-children id objects)))
|
||||||
|
|
||||||
|
(defn get-object-with-children
|
||||||
|
"Retrieve a list with an object and all of its children"
|
||||||
|
[id objects]
|
||||||
|
(map #(get objects %) (concat [id] (get-children id objects))))
|
||||||
|
|
||||||
(defn is-shape-grouped
|
(defn is-shape-grouped
|
||||||
"Checks if a shape is inside a group"
|
"Checks if a shape is inside a group"
|
||||||
[shape-id objects]
|
[shape-id objects]
|
||||||
|
@ -113,3 +152,55 @@
|
||||||
(lazy-seq (loopfn (rest ids))))))]
|
(lazy-seq (loopfn (rest ids))))))]
|
||||||
(loopfn (:shapes root))))
|
(loopfn (:shapes root))))
|
||||||
|
|
||||||
|
(defn clone-object
|
||||||
|
"Gets a copy of the object and all its children, with new ids
|
||||||
|
and with the parent-children links correctly set. Admits functions
|
||||||
|
to make more transformations to the cloned objects and the
|
||||||
|
original ones.
|
||||||
|
|
||||||
|
Returns the cloned object, the list of all new objects (including
|
||||||
|
the cloned one), and possibly a list of original objects modified."
|
||||||
|
|
||||||
|
([object parent-id objects update-new-object]
|
||||||
|
(clone-object object parent-id objects update-new-object identity))
|
||||||
|
|
||||||
|
([object parent-id objects update-new-object update-original-object]
|
||||||
|
(let [new-id (uuid/next)]
|
||||||
|
(loop [child-ids (seq (:shapes object))
|
||||||
|
new-direct-children []
|
||||||
|
new-children []
|
||||||
|
updated-children []]
|
||||||
|
|
||||||
|
(if (empty? child-ids)
|
||||||
|
(let [new-object (cond-> object
|
||||||
|
true
|
||||||
|
(assoc :id new-id
|
||||||
|
:parent-id parent-id)
|
||||||
|
|
||||||
|
(some? (:shapes object))
|
||||||
|
(assoc :shapes (map :id new-direct-children)))
|
||||||
|
|
||||||
|
new-object (update-new-object new-object object)
|
||||||
|
|
||||||
|
new-objects (concat [new-object] new-children)
|
||||||
|
|
||||||
|
updated-object (update-original-object object new-object)
|
||||||
|
|
||||||
|
updated-objects (if (= object updated-object)
|
||||||
|
updated-children
|
||||||
|
(concat [updated-object] updated-children))]
|
||||||
|
|
||||||
|
[new-object new-objects updated-objects])
|
||||||
|
|
||||||
|
(let [child-id (first child-ids)
|
||||||
|
child (get objects child-id)
|
||||||
|
|
||||||
|
[new-child new-child-objects updated-child-objects]
|
||||||
|
(clone-object child new-id objects update-new-object update-original-object)]
|
||||||
|
|
||||||
|
(recur
|
||||||
|
(next child-ids)
|
||||||
|
(concat new-direct-children [new-child])
|
||||||
|
(concat new-children new-child-objects)
|
||||||
|
(concat updated-children updated-child-objects))))))))
|
||||||
|
|
||||||
|
|
|
@ -52,4 +52,3 @@
|
||||||
;; (assoc obj :parent-id parent-id)))
|
;; (assoc obj :parent-id parent-id)))
|
||||||
;; objects)))))
|
;; objects)))))
|
||||||
|
|
||||||
|
|
||||||
|
|
3
frontend/resources/images/icons/component.svg
Normal file
3
frontend/resources/images/icons/component.svg
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 500 500">
|
||||||
|
<path d="M385 15l112 222a30 30 0 010 27L385 486a23 23 0 01-22 14H137c-5 1-9-1-13-3-4-3-7-6-9-11L3 264a30 30 0 010-27L115 15c2-4 5-8 9-10 4-3 8-4 13-4h226c5 0 9 1 13 4 4 2 7 6 9 10zM152 445h196l98-194-98-195H152L54 251zm98-139c28 0 50-25 50-55 0-31-22-55-50-55s-50 24-50 55c0 30 22 55 50 55z"/>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 366 B |
|
@ -288,7 +288,7 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"dashboard.grid.add-shared" : {
|
"dashboard.grid.add-shared" : {
|
||||||
"used-in" : [ "src/app/main/ui/workspace/header.cljs:146", "src/app/main/ui/dashboard/grid.cljs:166" ],
|
"used-in" : [ "src/app/main/ui/dashboard/grid.cljs:166", "src/app/main/ui/workspace/header.cljs:146" ],
|
||||||
"translations" : {
|
"translations" : {
|
||||||
"en" : "Add as Shared Library",
|
"en" : "Add as Shared Library",
|
||||||
"fr" : "",
|
"fr" : "",
|
||||||
|
@ -297,7 +297,7 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"dashboard.grid.add-shared-accept" : {
|
"dashboard.grid.add-shared-accept" : {
|
||||||
"used-in" : [ "src/app/main/ui/workspace/header.cljs:69", "src/app/main/ui/dashboard/grid.cljs:95" ],
|
"used-in" : [ "src/app/main/ui/dashboard/grid.cljs:95", "src/app/main/ui/workspace/header.cljs:69" ],
|
||||||
"translations" : {
|
"translations" : {
|
||||||
"en" : "Add as Shared Library",
|
"en" : "Add as Shared Library",
|
||||||
"fr" : "",
|
"fr" : "",
|
||||||
|
@ -306,7 +306,7 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"dashboard.grid.add-shared-hint" : {
|
"dashboard.grid.add-shared-hint" : {
|
||||||
"used-in" : [ "src/app/main/ui/workspace/header.cljs:68", "src/app/main/ui/dashboard/grid.cljs:94" ],
|
"used-in" : [ "src/app/main/ui/dashboard/grid.cljs:94", "src/app/main/ui/workspace/header.cljs:68" ],
|
||||||
"translations" : {
|
"translations" : {
|
||||||
"en" : "Once added as Shared Library, the assets of this file library will be available to be used among the rest of your files.",
|
"en" : "Once added as Shared Library, the assets of this file library will be available to be used among the rest of your files.",
|
||||||
"fr" : "",
|
"fr" : "",
|
||||||
|
@ -315,7 +315,7 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"dashboard.grid.add-shared-message" : {
|
"dashboard.grid.add-shared-message" : {
|
||||||
"used-in" : [ "src/app/main/ui/workspace/header.cljs:67", "src/app/main/ui/dashboard/grid.cljs:93" ],
|
"used-in" : [ "src/app/main/ui/dashboard/grid.cljs:93", "src/app/main/ui/workspace/header.cljs:67" ],
|
||||||
"translations" : {
|
"translations" : {
|
||||||
"en" : "Add “%s” as Shared Library",
|
"en" : "Add “%s” as Shared Library",
|
||||||
"fr" : "",
|
"fr" : "",
|
||||||
|
@ -342,7 +342,7 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"dashboard.grid.remove-shared" : {
|
"dashboard.grid.remove-shared" : {
|
||||||
"used-in" : [ "src/app/main/ui/workspace/header.cljs:144", "src/app/main/ui/dashboard/grid.cljs:165" ],
|
"used-in" : [ "src/app/main/ui/dashboard/grid.cljs:165", "src/app/main/ui/workspace/header.cljs:144" ],
|
||||||
"translations" : {
|
"translations" : {
|
||||||
"en" : "Remove as Shared Library",
|
"en" : "Remove as Shared Library",
|
||||||
"fr" : "",
|
"fr" : "",
|
||||||
|
@ -351,7 +351,7 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"dashboard.grid.remove-shared-accept" : {
|
"dashboard.grid.remove-shared-accept" : {
|
||||||
"used-in" : [ "src/app/main/ui/workspace/header.cljs:78", "src/app/main/ui/dashboard/grid.cljs:114" ],
|
"used-in" : [ "src/app/main/ui/dashboard/grid.cljs:114", "src/app/main/ui/workspace/header.cljs:78" ],
|
||||||
"translations" : {
|
"translations" : {
|
||||||
"en" : "Remove as Shared Library",
|
"en" : "Remove as Shared Library",
|
||||||
"fr" : "",
|
"fr" : "",
|
||||||
|
@ -360,7 +360,7 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"dashboard.grid.remove-shared-hint" : {
|
"dashboard.grid.remove-shared-hint" : {
|
||||||
"used-in" : [ "src/app/main/ui/workspace/header.cljs:77", "src/app/main/ui/dashboard/grid.cljs:113" ],
|
"used-in" : [ "src/app/main/ui/dashboard/grid.cljs:113", "src/app/main/ui/workspace/header.cljs:77" ],
|
||||||
"translations" : {
|
"translations" : {
|
||||||
"en" : "Once removed as Shared Library, the File Library of this file will stop being available to be used among the rest of your files.",
|
"en" : "Once removed as Shared Library, the File Library of this file will stop being available to be used among the rest of your files.",
|
||||||
"fr" : "",
|
"fr" : "",
|
||||||
|
@ -369,7 +369,7 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"dashboard.grid.remove-shared-message" : {
|
"dashboard.grid.remove-shared-message" : {
|
||||||
"used-in" : [ "src/app/main/ui/workspace/header.cljs:76", "src/app/main/ui/dashboard/grid.cljs:112" ],
|
"used-in" : [ "src/app/main/ui/dashboard/grid.cljs:112", "src/app/main/ui/workspace/header.cljs:76" ],
|
||||||
"translations" : {
|
"translations" : {
|
||||||
"en" : "Remove “%s” as Shared Library",
|
"en" : "Remove “%s” as Shared Library",
|
||||||
"fr" : "",
|
"fr" : "",
|
||||||
|
@ -621,6 +621,7 @@
|
||||||
"unused" : true
|
"unused" : true
|
||||||
},
|
},
|
||||||
"ds.button.save" : {
|
"ds.button.save" : {
|
||||||
|
"used-in" : [ "src/app/main/ui/workspace/sidebar/assets.cljs:66" ],
|
||||||
"translations" : {
|
"translations" : {
|
||||||
"en" : "Save",
|
"en" : "Save",
|
||||||
"fr" : "Sauvegarder",
|
"fr" : "Sauvegarder",
|
||||||
|
@ -774,7 +775,7 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"errors.media-type-mismatch" : {
|
"errors.media-type-mismatch" : {
|
||||||
"used-in" : [ "src/app/main/data/media.cljs:62", "src/app/main/data/workspace/persistence.cljs:352" ],
|
"used-in" : [ "src/app/main/data/workspace/persistence.cljs:352", "src/app/main/data/media.cljs:62" ],
|
||||||
"translations" : {
|
"translations" : {
|
||||||
"en" : "Seems that the contents of the image does not match the file extension.",
|
"en" : "Seems that the contents of the image does not match the file extension.",
|
||||||
"fr" : "",
|
"fr" : "",
|
||||||
|
@ -783,7 +784,7 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"errors.media-type-not-allowed" : {
|
"errors.media-type-not-allowed" : {
|
||||||
"used-in" : [ "src/app/main/data/media.cljs:59", "src/app/main/data/workspace/persistence.cljs:349" ],
|
"used-in" : [ "src/app/main/data/workspace/persistence.cljs:349", "src/app/main/data/media.cljs:59" ],
|
||||||
"translations" : {
|
"translations" : {
|
||||||
"en" : "Seems that this is not a valid image.",
|
"en" : "Seems that this is not a valid image.",
|
||||||
"fr" : "",
|
"fr" : "",
|
||||||
|
@ -828,7 +829,7 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"errors.unexpected-error" : {
|
"errors.unexpected-error" : {
|
||||||
"used-in" : [ "src/app/main/data/media.cljs:65", "src/app/main/ui/settings/change_email.cljs:51", "src/app/main/ui/auth/register.cljs:54", "src/app/main/ui/workspace/sidebar/options/exports.cljs:66" ],
|
"used-in" : [ "src/app/main/data/media.cljs:65", "src/app/main/ui/settings/change_email.cljs:51", "src/app/main/ui/workspace/sidebar/options/exports.cljs:66", "src/app/main/ui/auth/register.cljs:54" ],
|
||||||
"translations" : {
|
"translations" : {
|
||||||
"en" : "An unexpected error occurred.",
|
"en" : "An unexpected error occurred.",
|
||||||
"fr" : "Une erreur inattendue c'est produite",
|
"fr" : "Une erreur inattendue c'est produite",
|
||||||
|
@ -873,7 +874,7 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"media.loading" : {
|
"media.loading" : {
|
||||||
"used-in" : [ "src/app/main/data/media.cljs:44", "src/app/main/data/workspace/persistence.cljs:334" ],
|
"used-in" : [ "src/app/main/data/workspace/persistence.cljs:334", "src/app/main/data/media.cljs:44" ],
|
||||||
"translations" : {
|
"translations" : {
|
||||||
"en" : "Loading image...",
|
"en" : "Loading image...",
|
||||||
"fr" : "Chargement de l'image...",
|
"fr" : "Chargement de l'image...",
|
||||||
|
@ -882,6 +883,7 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"modal.create-color.new-color" : {
|
"modal.create-color.new-color" : {
|
||||||
|
"used-in" : [ "src/app/main/ui/workspace/sidebar/assets.cljs:59" ],
|
||||||
"translations" : {
|
"translations" : {
|
||||||
"en" : "New Color",
|
"en" : "New Color",
|
||||||
"fr" : "Nouvelle couleur",
|
"fr" : "Nouvelle couleur",
|
||||||
|
@ -1458,7 +1460,7 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"workspace.assets.assets" : {
|
"workspace.assets.assets" : {
|
||||||
"used-in" : [ "src/app/main/ui/workspace/sidebar/assets.cljs:374" ],
|
"used-in" : [ "src/app/main/ui/workspace/sidebar/assets.cljs:476" ],
|
||||||
"translations" : {
|
"translations" : {
|
||||||
"en" : "Assets",
|
"en" : "Assets",
|
||||||
"fr" : "",
|
"fr" : "",
|
||||||
|
@ -1467,7 +1469,7 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"workspace.assets.box-filter-all" : {
|
"workspace.assets.box-filter-all" : {
|
||||||
"used-in" : [ "src/app/main/ui/workspace/sidebar/assets.cljs:394" ],
|
"used-in" : [ "src/app/main/ui/workspace/sidebar/assets.cljs:496" ],
|
||||||
"translations" : {
|
"translations" : {
|
||||||
"en" : "All assets",
|
"en" : "All assets",
|
||||||
"fr" : "",
|
"fr" : "",
|
||||||
|
@ -1476,7 +1478,7 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"workspace.assets.box-filter-colors" : {
|
"workspace.assets.box-filter-colors" : {
|
||||||
"used-in" : [ "src/app/main/ui/workspace/sidebar/assets.cljs:396" ],
|
"used-in" : [ "src/app/main/ui/workspace/sidebar/assets.cljs:498" ],
|
||||||
"translations" : {
|
"translations" : {
|
||||||
"en" : "Colors",
|
"en" : "Colors",
|
||||||
"fr" : "",
|
"fr" : "",
|
||||||
|
@ -1485,7 +1487,7 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"workspace.assets.box-filter-graphics" : {
|
"workspace.assets.box-filter-graphics" : {
|
||||||
"used-in" : [ "src/app/main/ui/workspace/sidebar/assets.cljs:395" ],
|
"used-in" : [ "src/app/main/ui/workspace/sidebar/assets.cljs:497" ],
|
||||||
"translations" : {
|
"translations" : {
|
||||||
"en" : "Graphics",
|
"en" : "Graphics",
|
||||||
"fr" : "",
|
"fr" : "",
|
||||||
|
@ -1494,7 +1496,7 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"workspace.assets.colors" : {
|
"workspace.assets.colors" : {
|
||||||
"used-in" : [ "src/app/main/ui/workspace/sidebar/assets.cljs:247" ],
|
"used-in" : [ "src/app/main/ui/workspace/sidebar/assets.cljs:324" ],
|
||||||
"translations" : {
|
"translations" : {
|
||||||
"en" : "Colors",
|
"en" : "Colors",
|
||||||
"fr" : "",
|
"fr" : "",
|
||||||
|
@ -1502,8 +1504,17 @@
|
||||||
"es" : "Colores"
|
"es" : "Colores"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"workspace.assets.components" : {
|
||||||
|
"used-in" : [ "src/app/main/ui/workspace/sidebar/assets.cljs:106" ],
|
||||||
|
"translations" : {
|
||||||
|
"en" : "Components",
|
||||||
|
"fr" : "",
|
||||||
|
"ru" : "",
|
||||||
|
"es" : "Componentes"
|
||||||
|
}
|
||||||
|
},
|
||||||
"workspace.assets.delete" : {
|
"workspace.assets.delete" : {
|
||||||
"used-in" : [ "src/app/main/ui/workspace/sidebar/assets.cljs:125", "src/app/main/ui/workspace/sidebar/assets.cljs:224" ],
|
"used-in" : [ "src/app/main/ui/workspace/sidebar/assets.cljs:125", "src/app/main/ui/workspace/sidebar/assets.cljs:210", "src/app/main/ui/workspace/sidebar/assets.cljs:304" ],
|
||||||
"translations" : {
|
"translations" : {
|
||||||
"en" : "Delete",
|
"en" : "Delete",
|
||||||
"fr" : "",
|
"fr" : "",
|
||||||
|
@ -1512,7 +1523,7 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"workspace.assets.edit" : {
|
"workspace.assets.edit" : {
|
||||||
"used-in" : [ "src/app/main/ui/workspace/sidebar/assets.cljs:223" ],
|
"used-in" : [ "src/app/main/ui/workspace/sidebar/assets.cljs:303" ],
|
||||||
"translations" : {
|
"translations" : {
|
||||||
"en" : "Edit",
|
"en" : "Edit",
|
||||||
"fr" : "",
|
"fr" : "",
|
||||||
|
@ -1521,7 +1532,7 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"workspace.assets.file-library" : {
|
"workspace.assets.file-library" : {
|
||||||
"used-in" : [ "src/app/main/ui/workspace/sidebar/assets.cljs:309" ],
|
"used-in" : [ "src/app/main/ui/workspace/sidebar/assets.cljs:401" ],
|
||||||
"translations" : {
|
"translations" : {
|
||||||
"en" : "File library",
|
"en" : "File library",
|
||||||
"fr" : "",
|
"fr" : "",
|
||||||
|
@ -1530,7 +1541,7 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"workspace.assets.graphics" : {
|
"workspace.assets.graphics" : {
|
||||||
"used-in" : [ "src/app/main/ui/workspace/sidebar/assets.cljs:99" ],
|
"used-in" : [ "src/app/main/ui/workspace/sidebar/assets.cljs:184" ],
|
||||||
"translations" : {
|
"translations" : {
|
||||||
"en" : "Graphics",
|
"en" : "Graphics",
|
||||||
"fr" : "",
|
"fr" : "",
|
||||||
|
@ -1539,7 +1550,7 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"workspace.assets.libraries" : {
|
"workspace.assets.libraries" : {
|
||||||
"used-in" : [ "src/app/main/ui/workspace/sidebar/assets.cljs:377" ],
|
"used-in" : [ "src/app/main/ui/workspace/sidebar/assets.cljs:479" ],
|
||||||
"translations" : {
|
"translations" : {
|
||||||
"en" : "Libraries",
|
"en" : "Libraries",
|
||||||
"fr" : "",
|
"fr" : "",
|
||||||
|
@ -1548,7 +1559,7 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"workspace.assets.not-found" : {
|
"workspace.assets.not-found" : {
|
||||||
"used-in" : [ "src/app/main/ui/workspace/sidebar/assets.cljs:339" ],
|
"used-in" : [ "src/app/main/ui/workspace/sidebar/assets.cljs:440" ],
|
||||||
"translations" : {
|
"translations" : {
|
||||||
"en" : "No assets found",
|
"en" : "No assets found",
|
||||||
"fr" : "",
|
"fr" : "",
|
||||||
|
@ -1557,7 +1568,7 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"workspace.assets.rename" : {
|
"workspace.assets.rename" : {
|
||||||
"used-in" : [ "src/app/main/ui/workspace/sidebar/assets.cljs:222" ],
|
"used-in" : [ "src/app/main/ui/workspace/sidebar/assets.cljs:302" ],
|
||||||
"translations" : {
|
"translations" : {
|
||||||
"en" : "Rename",
|
"en" : "Rename",
|
||||||
"fr" : "",
|
"fr" : "",
|
||||||
|
@ -1566,7 +1577,7 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"workspace.assets.search" : {
|
"workspace.assets.search" : {
|
||||||
"used-in" : [ "src/app/main/ui/workspace/sidebar/assets.cljs:381" ],
|
"used-in" : [ "src/app/main/ui/workspace/sidebar/assets.cljs:483" ],
|
||||||
"translations" : {
|
"translations" : {
|
||||||
"en" : "Search assets",
|
"en" : "Search assets",
|
||||||
"fr" : "",
|
"fr" : "",
|
||||||
|
@ -1575,7 +1586,7 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"workspace.assets.shared" : {
|
"workspace.assets.shared" : {
|
||||||
"used-in" : [ "src/app/main/ui/workspace/sidebar/assets.cljs:311" ],
|
"used-in" : [ "src/app/main/ui/workspace/sidebar/assets.cljs:403" ],
|
||||||
"translations" : {
|
"translations" : {
|
||||||
"en" : "SHARED",
|
"en" : "SHARED",
|
||||||
"fr" : "",
|
"fr" : "",
|
||||||
|
|
|
@ -20,6 +20,8 @@ $color-warning: #FC8802;
|
||||||
$color-danger: #E65244;
|
$color-danger: #E65244;
|
||||||
$color-info: #59b9e2;
|
$color-info: #59b9e2;
|
||||||
$color-ocean: #4285f4;
|
$color-ocean: #4285f4;
|
||||||
|
$color-component: #76B0B8;
|
||||||
|
$color-component-highlight: #00E0FF;
|
||||||
|
|
||||||
// Gray scale
|
// Gray scale
|
||||||
$color-gray-10: #E3E3E3;
|
$color-gray-10: #E3E3E3;
|
||||||
|
|
|
@ -170,6 +170,19 @@
|
||||||
grid-auto-rows: 7vh;
|
grid-auto-rows: 7vh;
|
||||||
column-gap: 0.5rem;
|
column-gap: 0.5rem;
|
||||||
row-gap: 0.5rem;
|
row-gap: 0.5rem;
|
||||||
|
|
||||||
|
&.big {
|
||||||
|
grid-template-columns: 1fr 1fr;
|
||||||
|
grid-auto-rows: 10vh;
|
||||||
|
|
||||||
|
.grid-cell {
|
||||||
|
padding: $x-small;
|
||||||
|
|
||||||
|
& svg {
|
||||||
|
height: 10vh;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.grid-cell {
|
.grid-cell {
|
||||||
|
|
|
@ -20,42 +20,42 @@
|
||||||
margin-right: 8px;
|
margin-right: 8px;
|
||||||
width: 13px;
|
width: 13px;
|
||||||
}
|
}
|
||||||
|
|
||||||
&.group {
|
&.group {
|
||||||
&.open {
|
&.open {
|
||||||
.toggle-content {
|
.toggle-content {
|
||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
|
|
||||||
svg {
|
svg {
|
||||||
transform: rotate(270deg);
|
transform: rotate(270deg);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
background-color: $color-primary;
|
background-color: $color-primary;
|
||||||
|
|
||||||
svg {
|
svg {
|
||||||
fill: $color-gray-60 !important;
|
fill: $color-gray-60 !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.element-icon,
|
.element-icon,
|
||||||
.element-actions {
|
.element-actions {
|
||||||
|
|
||||||
svg {
|
svg {
|
||||||
fill: $color-gray-60;
|
fill: $color-gray-60;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.element-actions > * {
|
.element-actions > * {
|
||||||
display: flex;
|
display: flex;
|
||||||
}
|
}
|
||||||
|
|
||||||
span {
|
span {
|
||||||
color: $color-gray-60;
|
color: $color-gray-60;
|
||||||
}
|
}
|
||||||
|
|
||||||
.toggle-content {
|
.toggle-content {
|
||||||
svg {
|
svg {
|
||||||
fill: $color-gray-60;
|
fill: $color-gray-60;
|
||||||
|
@ -64,13 +64,12 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
&.selected {
|
&.selected {
|
||||||
|
|
||||||
svg {
|
svg {
|
||||||
fill: $color-primary;
|
fill: $color-primary;
|
||||||
}
|
}
|
||||||
|
|
||||||
.element-icon {
|
.element-icon {
|
||||||
|
|
||||||
svg {
|
svg {
|
||||||
fill: $color-primary;
|
fill: $color-primary;
|
||||||
}
|
}
|
||||||
|
@ -79,10 +78,10 @@
|
||||||
span {
|
span {
|
||||||
color: $color-primary;
|
color: $color-primary;
|
||||||
}
|
}
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
background-color: $color-primary;
|
background-color: $color-primary;
|
||||||
|
|
||||||
.element-icon,
|
.element-icon,
|
||||||
.element-actions {
|
.element-actions {
|
||||||
svg {
|
svg {
|
||||||
|
@ -95,20 +94,55 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&.drag-top {
|
&.drag-top {
|
||||||
border-top: 40px solid $color-gray-60 !important;
|
border-top: 40px solid $color-gray-60 !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
&.drag-bottom {
|
&.drag-bottom {
|
||||||
border-bottom: 40px solid $color-gray-60 !important;
|
border-bottom: 40px solid $color-gray-60 !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
&.drag-inside {
|
&.drag-inside {
|
||||||
border: 2px solid $color-primary !important;
|
border: 2px solid $color-primary !important;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.element-list li.component {
|
||||||
|
|
||||||
|
.element-list-body {
|
||||||
|
span.element-name {
|
||||||
|
color: $color-component;
|
||||||
|
}
|
||||||
|
|
||||||
|
svg {
|
||||||
|
fill: $color-component;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.selected {
|
||||||
|
span.element-name {
|
||||||
|
color: $color-component-highlight;
|
||||||
|
}
|
||||||
|
|
||||||
|
svg {
|
||||||
|
fill: $color-component-highlight;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background-color: $color-component-highlight;
|
||||||
|
|
||||||
|
span.element-name {
|
||||||
|
color: $color-gray-60;
|
||||||
|
}
|
||||||
|
|
||||||
|
svg {
|
||||||
|
fill: $color-gray-60;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.element-icon {
|
.element-icon {
|
||||||
svg {
|
svg {
|
||||||
fill: $color-gray-30;
|
fill: $color-gray-30;
|
||||||
|
@ -132,7 +166,7 @@ span.element-name {
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.element-actions {
|
.element-actions {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
|
@ -149,13 +183,13 @@ span.element-name {
|
||||||
> * {
|
> * {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.toggle-element,
|
.toggle-element,
|
||||||
.block-element {
|
.block-element {
|
||||||
left: 0;
|
left: 0;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 0;
|
top: 0;
|
||||||
|
|
||||||
&.selected {
|
&.selected {
|
||||||
display: flex;
|
display: flex;
|
||||||
|
|
||||||
|
@ -177,17 +211,17 @@ span.element-name {
|
||||||
.toggle-content {
|
.toggle-content {
|
||||||
margin-left: auto;
|
margin-left: auto;
|
||||||
width: 12px;
|
width: 12px;
|
||||||
|
|
||||||
svg {
|
svg {
|
||||||
fill: $color-gray-20;
|
fill: $color-gray-20;
|
||||||
transform: rotate(90deg);
|
transform: rotate(90deg);
|
||||||
width: 10px;
|
width: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
&.inverse {
|
&.inverse {
|
||||||
svg { transform: rotate(270deg); }
|
svg { transform: rotate(270deg); }
|
||||||
}
|
}
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
svg {
|
svg {
|
||||||
fill: $color-gray-60;
|
fill: $color-gray-60;
|
||||||
|
|
|
@ -22,12 +22,13 @@
|
||||||
[app.config :as cfg]
|
[app.config :as cfg]
|
||||||
[app.main.constants :as c]
|
[app.main.constants :as c]
|
||||||
[app.main.data.workspace.common :as dwc]
|
[app.main.data.workspace.common :as dwc]
|
||||||
|
[app.main.data.workspace.libraries :as dwl]
|
||||||
[app.main.data.workspace.notifications :as dwn]
|
[app.main.data.workspace.notifications :as dwn]
|
||||||
[app.main.data.workspace.persistence :as dwp]
|
[app.main.data.workspace.persistence :as dwp]
|
||||||
[app.main.data.workspace.selection :as dws]
|
[app.main.data.workspace.selection :as dws]
|
||||||
[app.main.data.workspace.texts :as dwtxt]
|
[app.main.data.workspace.texts :as dwtxt]
|
||||||
[app.main.data.workspace.transforms :as dwt]
|
[app.main.data.workspace.transforms :as dwt]
|
||||||
[app.main.data.colors :as dwl]
|
[app.main.data.colors :as mdc]
|
||||||
[app.main.repo :as rp]
|
[app.main.repo :as rp]
|
||||||
[app.main.store :as st]
|
[app.main.store :as st]
|
||||||
[app.main.streams :as ms]
|
[app.main.streams :as ms]
|
||||||
|
@ -47,10 +48,6 @@
|
||||||
(s/def ::set-of-string
|
(s/def ::set-of-string
|
||||||
(s/every string? :kind set?))
|
(s/every string? :kind set?))
|
||||||
|
|
||||||
;; --- Expose inner functions
|
|
||||||
|
|
||||||
(defn interrupt? [e] (= e :interrupt))
|
|
||||||
|
|
||||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||||
;; Workspace Initialization
|
;; Workspace Initialization
|
||||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||||
|
@ -949,7 +946,7 @@
|
||||||
ptk/WatchEvent
|
ptk/WatchEvent
|
||||||
(watch [_ state stream]
|
(watch [_ state stream]
|
||||||
(->> stream
|
(->> stream
|
||||||
(rx/filter interrupt?)
|
(rx/filter dwc/interrupt?)
|
||||||
(rx/take 1)
|
(rx/take 1)
|
||||||
(rx/map (constantly clear-edition-mode))))))
|
(rx/map (constantly clear-edition-mode))))))
|
||||||
|
|
||||||
|
@ -978,7 +975,7 @@
|
||||||
ptk/WatchEvent
|
ptk/WatchEvent
|
||||||
(watch [_ state stream]
|
(watch [_ state stream]
|
||||||
(let [cancel-event? (fn [event]
|
(let [cancel-event? (fn [event]
|
||||||
(interrupt? event))
|
(dwc/interrupt? event))
|
||||||
stoper (rx/filter (ptk/type? ::clear-drawing) stream)]
|
stoper (rx/filter (ptk/type? ::clear-drawing) stream)]
|
||||||
(->> (rx/filter cancel-event? stream)
|
(->> (rx/filter cancel-event? stream)
|
||||||
(rx/take 1)
|
(rx/take 1)
|
||||||
|
@ -1127,8 +1124,14 @@
|
||||||
(ptk/reify ::show-context-menu
|
(ptk/reify ::show-context-menu
|
||||||
ptk/UpdateEvent
|
ptk/UpdateEvent
|
||||||
(update [_ state]
|
(update [_ state]
|
||||||
(let [mdata {:position position
|
(let [page-id (:current-page-id state)
|
||||||
|
objects (dwc/lookup-page-objects state page-id)
|
||||||
|
root-id (cph/get-root-component (:id shape) objects)
|
||||||
|
root-shape (get objects root-id)
|
||||||
|
|
||||||
|
mdata {:position position
|
||||||
:shape shape
|
:shape shape
|
||||||
|
:root-shape root-shape
|
||||||
:selected (get-in state [:workspace-local :selected])}]
|
:selected (get-in state [:workspace-local :selected])}]
|
||||||
(-> state
|
(-> state
|
||||||
(assoc-in [:workspace-local :context-menu] mdata))))
|
(assoc-in [:workspace-local :context-menu] mdata))))
|
||||||
|
@ -1260,70 +1263,19 @@
|
||||||
;; GROUPS
|
;; GROUPS
|
||||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||||
|
|
||||||
(defn group-shape
|
|
||||||
[id frame-id selected selection-rect]
|
|
||||||
{:id id
|
|
||||||
:type :group
|
|
||||||
:name (name (gensym "Group-"))
|
|
||||||
:shapes []
|
|
||||||
:frame-id frame-id
|
|
||||||
:x (:x selection-rect)
|
|
||||||
:y (:y selection-rect)
|
|
||||||
:width (:width selection-rect)
|
|
||||||
:height (:height selection-rect)})
|
|
||||||
|
|
||||||
(def group-selected
|
(def group-selected
|
||||||
(ptk/reify ::group-selected
|
(ptk/reify ::group-selected
|
||||||
ptk/WatchEvent
|
ptk/WatchEvent
|
||||||
(watch [_ state stream]
|
(watch [_ state stream]
|
||||||
(let [id (uuid/next)
|
(let [page-id (:current-page-id state)
|
||||||
page-id (:current-page-id state)
|
|
||||||
objects (dwc/lookup-page-objects state page-id)
|
objects (dwc/lookup-page-objects state page-id)
|
||||||
selected (get-in state [:workspace-local :selected])
|
selected (get-in state [:workspace-local :selected])
|
||||||
items (->> selected
|
shapes (dws/shapes-for-grouping objects selected)]
|
||||||
(map #(get objects %))
|
(when-not (empty? shapes)
|
||||||
(filter #(not= :frame (:type %)))
|
(let [[group rchanges uchanges]
|
||||||
(map #(assoc % ::index (cph/position-on-parent (:id %) objects)))
|
(dws/prepare-create-group page-id shapes "Group-" false)]
|
||||||
(sort-by ::index))]
|
|
||||||
|
|
||||||
(when (not-empty items)
|
|
||||||
(let [selrect (geom/selection-rect items)
|
|
||||||
frame-id (-> items first :frame-id)
|
|
||||||
parent-id (-> items first :parent-id)
|
|
||||||
group (-> (group-shape id frame-id selected selrect)
|
|
||||||
(geom/setup selrect))
|
|
||||||
|
|
||||||
index (::index (first items))
|
|
||||||
|
|
||||||
rchanges [{:type :add-obj
|
|
||||||
:id id
|
|
||||||
:page-id page-id
|
|
||||||
:frame-id frame-id
|
|
||||||
:parent-id parent-id
|
|
||||||
:obj group
|
|
||||||
:index index}
|
|
||||||
{:type :mov-objects
|
|
||||||
:page-id page-id
|
|
||||||
:parent-id id
|
|
||||||
:shapes (->> items
|
|
||||||
(map :id)
|
|
||||||
(into #{})
|
|
||||||
(vec))}]
|
|
||||||
|
|
||||||
uchanges
|
|
||||||
(reduce (fn [res obj]
|
|
||||||
(conj res {:type :mov-objects
|
|
||||||
:page-id page-id
|
|
||||||
:parent-id (:parent-id obj)
|
|
||||||
:index (::index obj)
|
|
||||||
:shapes [(:id obj)]}))
|
|
||||||
[]
|
|
||||||
items)
|
|
||||||
|
|
||||||
uchanges (conj uchanges {:type :del-obj :id id :page-id page-id})]
|
|
||||||
|
|
||||||
(rx/of (dwc/commit-changes rchanges uchanges {:commit-local? true})
|
(rx/of (dwc/commit-changes rchanges uchanges {:commit-local? true})
|
||||||
(dws/select-shapes (d/ordered-set id)))))))))
|
(dws/select-shapes (d/ordered-set (:id group))))))))))
|
||||||
|
|
||||||
(def ungroup-selected
|
(def ungroup-selected
|
||||||
(ptk/reify ::ungroup-selected
|
(ptk/reify ::ungroup-selected
|
||||||
|
@ -1336,34 +1288,11 @@
|
||||||
group (get objects group-id)]
|
group (get objects group-id)]
|
||||||
(when (and (= 1 (count selected))
|
(when (and (= 1 (count selected))
|
||||||
(= (:type group) :group))
|
(= (:type group) :group))
|
||||||
(let [shapes (:shapes group)
|
(let [[rchanges uchanges]
|
||||||
parent-id (cph/get-parent group-id objects)
|
(dws/prepare-remove-group page-id group objects)]
|
||||||
parent (get objects parent-id)
|
|
||||||
index-in-parent (->> (:shapes parent)
|
|
||||||
(map-indexed vector)
|
|
||||||
(filter #(#{group-id} (second %)))
|
|
||||||
(ffirst))
|
|
||||||
rchanges [{:type :mov-objects
|
|
||||||
:page-id page-id
|
|
||||||
:parent-id parent-id
|
|
||||||
:shapes shapes
|
|
||||||
:index index-in-parent}]
|
|
||||||
uchanges [{:type :add-obj
|
|
||||||
:page-id page-id
|
|
||||||
:id group-id
|
|
||||||
:frame-id (:frame-id group)
|
|
||||||
:obj (assoc group :shapes [])}
|
|
||||||
{:type :mov-objects
|
|
||||||
:page-id page-id
|
|
||||||
:parent-id group-id
|
|
||||||
:shapes shapes}
|
|
||||||
{:type :mov-objects
|
|
||||||
:page-id page-id
|
|
||||||
:parent-id parent-id
|
|
||||||
:shapes [group-id]
|
|
||||||
:index index-in-parent}]]
|
|
||||||
(rx/of (dwc/commit-changes rchanges uchanges {:commit-local? true}))))))))
|
(rx/of (dwc/commit-changes rchanges uchanges {:commit-local? true}))))))))
|
||||||
|
|
||||||
|
|
||||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||||
;; Interactions
|
;; Interactions
|
||||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||||
|
@ -1506,6 +1435,7 @@
|
||||||
"+" #(st/emit! (increase-zoom nil))
|
"+" #(st/emit! (increase-zoom nil))
|
||||||
"-" #(st/emit! (decrease-zoom nil))
|
"-" #(st/emit! (decrease-zoom nil))
|
||||||
"ctrl+g" #(st/emit! group-selected)
|
"ctrl+g" #(st/emit! group-selected)
|
||||||
|
"ctrl+k" #(st/emit! dwl/add-component)
|
||||||
"shift+g" #(st/emit! ungroup-selected)
|
"shift+g" #(st/emit! ungroup-selected)
|
||||||
"shift+0" #(st/emit! reset-zoom)
|
"shift+0" #(st/emit! reset-zoom)
|
||||||
"shift+1" #(st/emit! zoom-to-fit-all)
|
"shift+1" #(st/emit! zoom-to-fit-all)
|
||||||
|
@ -1537,5 +1467,5 @@
|
||||||
"right" #(st/emit! (dwt/move-selected :right false))
|
"right" #(st/emit! (dwt/move-selected :right false))
|
||||||
"left" #(st/emit! (dwt/move-selected :left false))
|
"left" #(st/emit! (dwt/move-selected :left false))
|
||||||
|
|
||||||
"i" #(st/emit! (dwl/picker-for-selected-shape ))})
|
"i" #(st/emit! (mdc/picker-for-selected-shape ))})
|
||||||
|
|
||||||
|
|
|
@ -44,6 +44,11 @@
|
||||||
([state page-id]
|
([state page-id]
|
||||||
(get-in state [:workspace-data :pages-index page-id :options])))
|
(get-in state [:workspace-data :pages-index page-id :options])))
|
||||||
|
|
||||||
|
(defn interrupt? [e] (= e :interrupt))
|
||||||
|
|
||||||
|
(defn lookup-component-objects
|
||||||
|
([state component-id]
|
||||||
|
(get-in state [:workspace-data :components component-id :objects])))
|
||||||
|
|
||||||
|
|
||||||
;; --- Changes Handling
|
;; --- Changes Handling
|
||||||
|
@ -454,3 +459,4 @@
|
||||||
objects (lookup-page-objects state page-id)
|
objects (lookup-page-objects state page-id)
|
||||||
[rchanges uchanges] (impl-gen-changes objects page-id (seq ids))]
|
[rchanges uchanges] (impl-gen-changes objects page-id (seq ids))]
|
||||||
(rx/of (commit-changes rchanges uchanges {:commit-local? true})))))))
|
(rx/of (commit-changes rchanges uchanges {:commit-local? true})))))))
|
||||||
|
|
||||||
|
|
|
@ -12,12 +12,18 @@
|
||||||
[app.common.data :as d]
|
[app.common.data :as d]
|
||||||
[app.common.spec :as us]
|
[app.common.spec :as us]
|
||||||
[app.common.uuid :as uuid]
|
[app.common.uuid :as uuid]
|
||||||
|
[app.common.pages-helpers :as cph]
|
||||||
|
[app.common.geom.point :as gpt]
|
||||||
|
[app.common.geom.shapes :as geom]
|
||||||
[app.main.data.workspace.common :as dwc]
|
[app.main.data.workspace.common :as dwc]
|
||||||
|
[app.main.data.workspace.selection :as dws]
|
||||||
[app.common.pages :as cp]
|
[app.common.pages :as cp]
|
||||||
[app.main.repo :as rp]
|
[app.main.repo :as rp]
|
||||||
[app.main.store :as st]
|
[app.main.store :as st]
|
||||||
|
[app.main.streams :as ms]
|
||||||
[app.util.color :as color]
|
[app.util.color :as color]
|
||||||
[app.util.i18n :refer [tr]]
|
[app.util.i18n :refer [tr]]
|
||||||
|
[app.util.router :as rt]
|
||||||
[beicon.core :as rx]
|
[beicon.core :as rx]
|
||||||
[cljs.spec.alpha :as s]
|
[cljs.spec.alpha :as s]
|
||||||
[potok.core :as ptk]))
|
[potok.core :as ptk]))
|
||||||
|
@ -68,7 +74,7 @@
|
||||||
(rx/of (dwc/commit-changes [rchg] [uchg] {:commit-local? true}))))))
|
(rx/of (dwc/commit-changes [rchg] [uchg] {:commit-local? true}))))))
|
||||||
|
|
||||||
(defn delete-color
|
(defn delete-color
|
||||||
[{:keys [id] :as color}]
|
[{:keys [id] :as params}]
|
||||||
(us/assert ::us/uuid id)
|
(us/assert ::us/uuid id)
|
||||||
(ptk/reify ::delete-color
|
(ptk/reify ::delete-color
|
||||||
ptk/WatchEvent
|
ptk/WatchEvent
|
||||||
|
@ -94,7 +100,7 @@
|
||||||
|
|
||||||
|
|
||||||
(defn delete-media
|
(defn delete-media
|
||||||
[{:keys [id] :as media}]
|
[{:keys [id] :as params}]
|
||||||
(us/assert ::us/uuid id)
|
(us/assert ::us/uuid id)
|
||||||
(ptk/reify ::delete-media
|
(ptk/reify ::delete-media
|
||||||
ptk/WatchEvent
|
ptk/WatchEvent
|
||||||
|
@ -106,3 +112,502 @@
|
||||||
:object prev}]
|
:object prev}]
|
||||||
(rx/of (dwc/commit-changes [rchg] [uchg] {:commit-local? true}))))))
|
(rx/of (dwc/commit-changes [rchg] [uchg] {:commit-local? true}))))))
|
||||||
|
|
||||||
|
(declare make-component-shape)
|
||||||
|
|
||||||
|
(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]
|
||||||
|
(make-component-shape group nil 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 :shape-ref
|
||||||
|
:val (:shape-ref updated-shape)}]})
|
||||||
|
updated-shapes))
|
||||||
|
|
||||||
|
uchanges (conj uchanges
|
||||||
|
{:type :del-component
|
||||||
|
:id (:id new-shape)})
|
||||||
|
|
||||||
|
uchanges (into uchanges
|
||||||
|
(map (fn [updated-shape]
|
||||||
|
{:type :mod-obj
|
||||||
|
:page-id page-id
|
||||||
|
:id (:id updated-shape)
|
||||||
|
:operations [{:type :set
|
||||||
|
:attr :component-id
|
||||||
|
:val nil}
|
||||||
|
{:type :set
|
||||||
|
:attr :component-file
|
||||||
|
:val nil}
|
||||||
|
{:type :set
|
||||||
|
:attr :shape-ref
|
||||||
|
:val nil}]})
|
||||||
|
updated-shapes))]
|
||||||
|
|
||||||
|
(rx/of (dwc/commit-changes rchanges uchanges {:commit-local? true})
|
||||||
|
(dws/select-shapes (d/ordered-set (:id group))))))))))
|
||||||
|
|
||||||
|
(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 parent-id objects]
|
||||||
|
(let [update-new-shape (fn [new-shape original-shape]
|
||||||
|
(assoc new-shape :frame-id nil))
|
||||||
|
|
||||||
|
update-original-shape (fn [original-shape new-shape]
|
||||||
|
(cond-> original-shape
|
||||||
|
true
|
||||||
|
(assoc :shape-ref (:id new-shape))
|
||||||
|
|
||||||
|
(nil? (:parent-id new-shape))
|
||||||
|
(assoc :component-id (:id new-shape))))]
|
||||||
|
|
||||||
|
(cph/clone-object shape parent-id objects update-new-shape update-original-shape)))
|
||||||
|
|
||||||
|
(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]
|
||||||
|
(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))
|
||||||
|
mouse-pos @ms/mouse-position
|
||||||
|
delta (gpt/subtract mouse-pos orig-pos)
|
||||||
|
|
||||||
|
_ (js/console.log "orig-pos" (clj->js orig-pos))
|
||||||
|
_ (js/console.log "mouse-pos" (clj->js mouse-pos))
|
||||||
|
_ (js/console.log "delta" (clj->js delta))
|
||||||
|
|
||||||
|
page-id (:current-page-id state)
|
||||||
|
objects (dwc/lookup-page-objects state page-id)
|
||||||
|
unames (atom (dwc/retrieve-used-names objects))
|
||||||
|
|
||||||
|
all-frames (cph/select-frames objects)
|
||||||
|
|
||||||
|
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
|
||||||
|
(dwc/calculate-frame-overlap all-frames $))
|
||||||
|
(assoc $ :parent-id
|
||||||
|
(or (:parent-id $) (:frame-id $)))
|
||||||
|
(assoc $ :shape-ref (:id original-shape)))
|
||||||
|
|
||||||
|
(nil? (:parent-id original-shape))
|
||||||
|
(assoc :component-id (:id original-shape))
|
||||||
|
|
||||||
|
(and (nil? (:parent-id original-shape)) (some? file-id))
|
||||||
|
(assoc :component-file file-id))))
|
||||||
|
|
||||||
|
[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)
|
||||||
|
root-id (cph/get-root-component id objects)
|
||||||
|
|
||||||
|
shapes (cph/get-object-with-children root-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))))))
|
||||||
|
|
||||||
|
(declare generate-sync-file)
|
||||||
|
(declare generate-sync-page)
|
||||||
|
(declare generate-sync-shape-and-children)
|
||||||
|
(declare generate-sync-shape)
|
||||||
|
(declare remove-component-and-ref)
|
||||||
|
(declare remove-ref)
|
||||||
|
(declare update-attrs)
|
||||||
|
(declare sync-attrs)
|
||||||
|
(declare calc-new-pos)
|
||||||
|
|
||||||
|
(defn reset-component
|
||||||
|
[id]
|
||||||
|
(us/assert ::us/uuid id)
|
||||||
|
(ptk/reify ::reset-component
|
||||||
|
ptk/WatchEvent
|
||||||
|
(watch [_ state stream]
|
||||||
|
(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)
|
||||||
|
root-id (cph/get-root-component id objects)
|
||||||
|
root-shape (get objects id)
|
||||||
|
file-id (get root-shape :component-file)
|
||||||
|
|
||||||
|
components
|
||||||
|
(if (nil? file-id)
|
||||||
|
(get-in state [:workspace-data :components])
|
||||||
|
(get-in state [:workspace-libraries file-id :data :components]))
|
||||||
|
|
||||||
|
[rchanges uchanges]
|
||||||
|
(generate-sync-shape-and-children root-shape page components)]
|
||||||
|
|
||||||
|
(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]
|
||||||
|
(let [page-id (:current-page-id state)
|
||||||
|
objects (dwc/lookup-page-objects state page-id)
|
||||||
|
root-id (cph/get-root-component id objects)
|
||||||
|
root-shape (get objects id)
|
||||||
|
|
||||||
|
component-id (get root-shape :component-id)
|
||||||
|
component-objs (dwc/lookup-component-objects state component-id)
|
||||||
|
component-obj (get component-objs component-id)
|
||||||
|
|
||||||
|
;; Clone again the original shape and its children, maintaing
|
||||||
|
;; the ids of the cloned shapes. If the original shape has some
|
||||||
|
;; new child shapes, the cloned ones will have new generated ids.
|
||||||
|
update-new-shape (fn [new-shape original-shape]
|
||||||
|
(cond-> new-shape
|
||||||
|
true
|
||||||
|
(assoc :frame-id nil)
|
||||||
|
|
||||||
|
(some? (:shape-ref original-shape))
|
||||||
|
(assoc :id (:shape-ref original-shape))))
|
||||||
|
|
||||||
|
[new-shape new-shapes _]
|
||||||
|
(cph/clone-object root-shape nil objects update-new-shape)
|
||||||
|
|
||||||
|
rchanges [{:type :update-component
|
||||||
|
:id component-id
|
||||||
|
:name (:name new-shape)
|
||||||
|
:shapes new-shapes}]
|
||||||
|
|
||||||
|
uchanges [{:type :update-component
|
||||||
|
:id component-id
|
||||||
|
:name (:name component-obj)
|
||||||
|
:shapes (vals component-objs)}]]
|
||||||
|
|
||||||
|
(rx/of (dwc/commit-changes rchanges uchanges {:commit-local? true}))))))
|
||||||
|
|
||||||
|
(defn sync-file
|
||||||
|
[{:keys [file-id] :as params}]
|
||||||
|
(us/assert (s/nilable ::us/uuid) file-id)
|
||||||
|
(ptk/reify ::sync-file
|
||||||
|
ptk/WatchEvent
|
||||||
|
(watch [_ state stream]
|
||||||
|
(let [[rchanges uchanges] (generate-sync-file state file-id)]
|
||||||
|
(rx/of (dwc/commit-changes rchanges uchanges {:commit-local? true}))))))
|
||||||
|
|
||||||
|
(defn- generate-sync-file
|
||||||
|
[state file-id]
|
||||||
|
(let [components
|
||||||
|
(if (nil? file-id)
|
||||||
|
(get-in state [:workspace-data :components])
|
||||||
|
(get-in state [:workspace-libraries file-id :data :components]))]
|
||||||
|
(loop [pages (seq (vals (get-in state [:workspace-data :pages-index])))
|
||||||
|
rchanges []
|
||||||
|
uchanges []]
|
||||||
|
(let [page (first pages)]
|
||||||
|
(if (nil? page)
|
||||||
|
[rchanges uchanges]
|
||||||
|
(let [[page-rchanges page-uchanges]
|
||||||
|
(generate-sync-page page components)]
|
||||||
|
(recur (next pages)
|
||||||
|
(concat rchanges page-rchanges)
|
||||||
|
(concat uchanges page-uchanges))))))))
|
||||||
|
|
||||||
|
(defn- generate-sync-page
|
||||||
|
[page components]
|
||||||
|
(let [linked-shapes
|
||||||
|
(cph/select-objects #(some? (:component-id %)) page)]
|
||||||
|
(loop [shapes (seq linked-shapes)
|
||||||
|
rchanges []
|
||||||
|
uchanges []]
|
||||||
|
(let [shape (first shapes)]
|
||||||
|
(if (nil? shape)
|
||||||
|
[rchanges uchanges]
|
||||||
|
(let [[shape-rchanges shape-uchanges]
|
||||||
|
(generate-sync-shape-and-children shape page components)]
|
||||||
|
(recur (next shapes)
|
||||||
|
(concat rchanges shape-rchanges)
|
||||||
|
(concat uchanges shape-uchanges))))))))
|
||||||
|
|
||||||
|
(defn- generate-sync-shape-and-children
|
||||||
|
[root-shape page components]
|
||||||
|
(let [objects (get page :objects)
|
||||||
|
all-shapes (cph/get-object-with-children (:id root-shape) objects)
|
||||||
|
component (get components (:component-id root-shape))
|
||||||
|
root-component (get-in component [:objects (:shape-ref root-shape)])]
|
||||||
|
(loop [shapes (seq all-shapes)
|
||||||
|
rchanges []
|
||||||
|
uchanges []]
|
||||||
|
(let [shape (first shapes)]
|
||||||
|
(if (nil? shape)
|
||||||
|
[rchanges uchanges]
|
||||||
|
(let [[shape-rchanges shape-uchanges]
|
||||||
|
(generate-sync-shape shape root-shape root-component page component)]
|
||||||
|
(recur (next shapes)
|
||||||
|
(concat rchanges shape-rchanges)
|
||||||
|
(concat uchanges shape-uchanges))))))))
|
||||||
|
|
||||||
|
(defn- generate-sync-shape
|
||||||
|
[shape root-shape root-component page component]
|
||||||
|
(if (nil? component)
|
||||||
|
(remove-component-and-ref shape page)
|
||||||
|
(let [component-shape (get (:objects component) (:shape-ref shape))]
|
||||||
|
(if (nil? component-shape)
|
||||||
|
(remove-ref shape page)
|
||||||
|
(update-attrs shape component-shape root-shape root-component page)))))
|
||||||
|
|
||||||
|
(defn- remove-component-and-ref
|
||||||
|
[shape page]
|
||||||
|
[[{:type :mod-obj
|
||||||
|
:page-id (:id page)
|
||||||
|
:id (:id shape)
|
||||||
|
:operations [{:type :set
|
||||||
|
:attr :component-id
|
||||||
|
:val nil}
|
||||||
|
{:type :set
|
||||||
|
:attr :component-file
|
||||||
|
:val nil}
|
||||||
|
{:type :set
|
||||||
|
:attr :shape-ref
|
||||||
|
:val nil}]}]
|
||||||
|
[{:type :mod-obj
|
||||||
|
:page-id (:id page)
|
||||||
|
:id (:id shape)
|
||||||
|
:operations [{:type :set
|
||||||
|
:attr :component-id
|
||||||
|
:val (:component-id shape)}
|
||||||
|
{:type :set
|
||||||
|
:attr :component-file
|
||||||
|
:val (:component-file shape)}
|
||||||
|
{:type :set
|
||||||
|
:attr :shape-ref
|
||||||
|
:val (:shape-ref shape)}]}]])
|
||||||
|
|
||||||
|
(defn- remove-ref
|
||||||
|
[shape page]
|
||||||
|
[[{:type :mod-obj
|
||||||
|
:page-id (:id page)
|
||||||
|
:id (:id shape)
|
||||||
|
:operations [{:type :set
|
||||||
|
:attr :shape-ref
|
||||||
|
:val nil}]}]
|
||||||
|
[{:type :mod-obj
|
||||||
|
:page-id (:id page)
|
||||||
|
:id (:id shape)
|
||||||
|
:operations [{:type :set
|
||||||
|
:attr :shape-ref
|
||||||
|
:val (:shape-ref shape)}]}]])
|
||||||
|
|
||||||
|
(defn- update-attrs
|
||||||
|
[shape component-shape root-shape root-component page]
|
||||||
|
(let [new-pos (calc-new-pos shape component-shape root-shape root-component)]
|
||||||
|
(loop [attrs (seq sync-attrs)
|
||||||
|
roperations [{:type :set
|
||||||
|
:attr :x
|
||||||
|
:val (:x new-pos)}
|
||||||
|
{:type :set
|
||||||
|
:attr :y
|
||||||
|
:val (:y new-pos)}]
|
||||||
|
uoperations [{:type :set
|
||||||
|
:attr :x
|
||||||
|
:val (:x shape)}
|
||||||
|
{:type :set
|
||||||
|
:attr :y
|
||||||
|
:val (:y shape)}]]
|
||||||
|
|
||||||
|
(let [attr (first attrs)]
|
||||||
|
(if (nil? attr)
|
||||||
|
(let [rchanges [{:type :mod-obj
|
||||||
|
:page-id (:id page)
|
||||||
|
:id (:id shape)
|
||||||
|
:operations roperations}]
|
||||||
|
uchanges [{:type :mod-obj
|
||||||
|
:page-id (:id page)
|
||||||
|
:id (:id shape)
|
||||||
|
:operations uoperations}]]
|
||||||
|
[rchanges uchanges])
|
||||||
|
(if-not (contains? shape attr)
|
||||||
|
(recur (next attrs)
|
||||||
|
roperations
|
||||||
|
uoperations)
|
||||||
|
(let [roperation {:type :set
|
||||||
|
:attr attr
|
||||||
|
:val (get component-shape attr)}
|
||||||
|
uoperation {:type :set
|
||||||
|
:attr attr
|
||||||
|
:val (get shape attr)}]
|
||||||
|
(recur (next attrs)
|
||||||
|
(conj roperations roperation)
|
||||||
|
(conj uoperations uoperation)))))))))
|
||||||
|
|
||||||
|
(def sync-attrs [:content
|
||||||
|
:fill-color
|
||||||
|
:fill-color-ref-file
|
||||||
|
:fill-color-ref-id
|
||||||
|
:fill-opacity
|
||||||
|
:font-family
|
||||||
|
:font-size
|
||||||
|
:font-style
|
||||||
|
:font-weight
|
||||||
|
:letter-spacing
|
||||||
|
:line-height
|
||||||
|
:proportion
|
||||||
|
:rx
|
||||||
|
:ry
|
||||||
|
:stroke-color
|
||||||
|
:stroke-color-ref-file
|
||||||
|
:stroke-color-ref-id
|
||||||
|
:stroke-opacity
|
||||||
|
:stroke-style
|
||||||
|
:stroke-width
|
||||||
|
:stroke-alignment
|
||||||
|
:text-align
|
||||||
|
:width
|
||||||
|
:height
|
||||||
|
:interactions
|
||||||
|
:points
|
||||||
|
:transform])
|
||||||
|
|
||||||
|
(defn- calc-new-pos
|
||||||
|
[shape component-shape root-shape root-component]
|
||||||
|
(let [root-pos (gpt/point (:x root-shape) (:y root-shape))
|
||||||
|
root-component-pos (gpt/point (:x root-component) (:y root-component))
|
||||||
|
component-pos (gpt/point (:x component-shape) (:y component-shape))
|
||||||
|
delta (gpt/subtract component-pos root-component-pos)
|
||||||
|
shape-pos (gpt/point (:x shape) (:y shape))
|
||||||
|
new-pos (gpt/add root-pos delta)]
|
||||||
|
new-pos))
|
||||||
|
|
||||||
|
|
|
@ -33,33 +33,6 @@
|
||||||
(s/def ::set-of-string
|
(s/def ::set-of-string
|
||||||
(s/every string? :kind set?))
|
(s/every string? :kind set?))
|
||||||
|
|
||||||
;; Duplicate from workspace.
|
|
||||||
;; FIXME: Move these functions to a common place
|
|
||||||
|
|
||||||
(defn interrupt? [e] (= e :interrupt))
|
|
||||||
|
|
||||||
(defn- retrieve-used-names
|
|
||||||
[objects]
|
|
||||||
(into #{} (map :name) (vals objects)))
|
|
||||||
|
|
||||||
(defn- extract-numeric-suffix
|
|
||||||
[basename]
|
|
||||||
(if-let [[match p1 p2] (re-find #"(.*)-([0-9]+)$" basename)]
|
|
||||||
[p1 (+ 1 (d/parse-integer p2))]
|
|
||||||
[basename 1]))
|
|
||||||
|
|
||||||
(defn- generate-unique-name
|
|
||||||
"A unique name generator"
|
|
||||||
[used basename]
|
|
||||||
(s/assert ::set-of-string used)
|
|
||||||
(s/assert ::us/string basename)
|
|
||||||
(let [[prefix initial] (extract-numeric-suffix basename)]
|
|
||||||
(loop [counter initial]
|
|
||||||
(let [candidate (str prefix "-" counter)]
|
|
||||||
(if (contains? used candidate)
|
|
||||||
(recur (inc counter))
|
|
||||||
candidate)))))
|
|
||||||
|
|
||||||
;; --- Selection Rect
|
;; --- Selection Rect
|
||||||
|
|
||||||
(declare select-shapes-by-current-selrect)
|
(declare select-shapes-by-current-selrect)
|
||||||
|
@ -88,7 +61,7 @@
|
||||||
(ptk/reify ::handle-selection
|
(ptk/reify ::handle-selection
|
||||||
ptk/WatchEvent
|
ptk/WatchEvent
|
||||||
(watch [_ state stream]
|
(watch [_ state stream]
|
||||||
(let [stoper (rx/filter #(or (interrupt? %)
|
(let [stoper (rx/filter #(or (dwc/interrupt? %)
|
||||||
(ms/mouse-up? %))
|
(ms/mouse-up? %))
|
||||||
stream)]
|
stream)]
|
||||||
(rx/concat
|
(rx/concat
|
||||||
|
@ -183,6 +156,88 @@
|
||||||
(rx/of deselect-all (select-shape (:id selected))))))))
|
(rx/of deselect-all (select-shape (:id selected))))))))
|
||||||
|
|
||||||
|
|
||||||
|
;; --- Group shapes
|
||||||
|
|
||||||
|
(defn shapes-for-grouping
|
||||||
|
[objects selected]
|
||||||
|
(->> selected
|
||||||
|
(map #(get objects %))
|
||||||
|
(filter #(not= :frame (:type %)))
|
||||||
|
(map #(assoc % ::index (cph/position-on-parent (:id %) objects)))
|
||||||
|
(sort-by ::index)))
|
||||||
|
|
||||||
|
(defn- make-group
|
||||||
|
[shapes prefix keep-name]
|
||||||
|
(let [selrect (geom/selection-rect shapes)
|
||||||
|
frame-id (-> shapes first :frame-id)
|
||||||
|
parent-id (-> shapes first :parent-id)
|
||||||
|
group-name (if (and keep-name
|
||||||
|
(= (count shapes) 1)
|
||||||
|
(= (:type (first shapes)) :group))
|
||||||
|
(:name (first shapes))
|
||||||
|
(name (gensym prefix)))]
|
||||||
|
(-> (cp/make-minimal-group frame-id selrect group-name)
|
||||||
|
(geom/setup selrect)
|
||||||
|
(assoc :shapes (map :id shapes)))))
|
||||||
|
|
||||||
|
(defn prepare-create-group
|
||||||
|
[page-id shapes prefix keep-name]
|
||||||
|
(let [group (make-group shapes prefix keep-name)
|
||||||
|
rchanges [{:type :add-obj
|
||||||
|
:id (:id group)
|
||||||
|
:page-id page-id
|
||||||
|
:frame-id (:frame-id (first shapes))
|
||||||
|
:parent-id (:parent-id (first shapes))
|
||||||
|
:obj group
|
||||||
|
:index (::index (first shapes))}
|
||||||
|
{:type :mov-objects
|
||||||
|
:page-id page-id
|
||||||
|
:parent-id (:id group)
|
||||||
|
:shapes (map :id shapes)}]
|
||||||
|
|
||||||
|
uchanges (conj
|
||||||
|
(map (fn [obj] {:type :mov-objects
|
||||||
|
:page-id page-id
|
||||||
|
:parent-id (:parent-id obj)
|
||||||
|
:index (::index obj)
|
||||||
|
:shapes [(:id obj)]})
|
||||||
|
shapes)
|
||||||
|
{:type :del-obj
|
||||||
|
:id (:id group)
|
||||||
|
:page-id page-id})]
|
||||||
|
[group rchanges uchanges]))
|
||||||
|
|
||||||
|
(defn prepare-remove-group
|
||||||
|
[page-id group objects]
|
||||||
|
(let [shapes (:shapes group)
|
||||||
|
parent-id (cph/get-parent (:id group) objects)
|
||||||
|
parent (get objects parent-id)
|
||||||
|
index-in-parent (->> (:shapes parent)
|
||||||
|
(map-indexed vector)
|
||||||
|
(filter #(#{(:id group)} (second %)))
|
||||||
|
(ffirst))
|
||||||
|
rchanges [{:type :mov-objects
|
||||||
|
:page-id page-id
|
||||||
|
:parent-id parent-id
|
||||||
|
:shapes shapes
|
||||||
|
:index index-in-parent}]
|
||||||
|
uchanges [{:type :add-obj
|
||||||
|
:page-id page-id
|
||||||
|
:id (:id group)
|
||||||
|
:frame-id (:frame-id group)
|
||||||
|
:obj (assoc group :shapes [])}
|
||||||
|
{:type :mov-objects
|
||||||
|
:page-id page-id
|
||||||
|
:parent-id (:id group)
|
||||||
|
:shapes shapes}
|
||||||
|
{:type :mov-objects
|
||||||
|
:page-id page-id
|
||||||
|
:parent-id parent-id
|
||||||
|
:shapes [(:id group)]
|
||||||
|
:index index-in-parent}]]
|
||||||
|
[rchanges uchanges]))
|
||||||
|
|
||||||
|
|
||||||
;; --- Duplicate Shapes
|
;; --- Duplicate Shapes
|
||||||
(declare prepare-duplicate-change)
|
(declare prepare-duplicate-change)
|
||||||
(declare prepare-duplicate-frame-change)
|
(declare prepare-duplicate-frame-change)
|
||||||
|
@ -218,7 +273,7 @@
|
||||||
(defn- prepare-duplicate-shape-change
|
(defn- prepare-duplicate-shape-change
|
||||||
[objects page-id names obj delta frame-id parent-id]
|
[objects page-id names obj delta frame-id parent-id]
|
||||||
(let [id (uuid/next)
|
(let [id (uuid/next)
|
||||||
name (generate-unique-name names (:name obj))
|
name (dwc/generate-unique-name names (:name obj))
|
||||||
renamed-obj (assoc obj :id id :name name)
|
renamed-obj (assoc obj :id id :name name)
|
||||||
moved-obj (geom/move renamed-obj delta)
|
moved-obj (geom/move renamed-obj delta)
|
||||||
frames (cph/select-frames objects)
|
frames (cph/select-frames objects)
|
||||||
|
@ -258,7 +313,7 @@
|
||||||
(defn- prepare-duplicate-frame-change
|
(defn- prepare-duplicate-frame-change
|
||||||
[objects page-id names obj delta]
|
[objects page-id names obj delta]
|
||||||
(let [frame-id (uuid/next)
|
(let [frame-id (uuid/next)
|
||||||
frame-name (generate-unique-name names (:name obj))
|
frame-name (dwc/generate-unique-name names (:name obj))
|
||||||
sch (->> (map #(get objects %) (:shapes obj))
|
sch (->> (map #(get objects %) (:shapes obj))
|
||||||
(mapcat #(prepare-duplicate-shape-change objects page-id names % delta frame-id frame-id)))
|
(mapcat #(prepare-duplicate-shape-change objects page-id names % delta frame-id frame-id)))
|
||||||
|
|
||||||
|
@ -287,7 +342,7 @@
|
||||||
|
|
||||||
selected (get-in state [:workspace-local :selected])
|
selected (get-in state [:workspace-local :selected])
|
||||||
delta (gpt/point 0 0)
|
delta (gpt/point 0 0)
|
||||||
unames (retrieve-used-names objects)
|
unames (dwc/retrieve-used-names objects)
|
||||||
|
|
||||||
rchanges (prepare-duplicate-changes objects page-id unames selected delta)
|
rchanges (prepare-duplicate-changes objects page-id unames selected delta)
|
||||||
uchanges (mapv #(array-map :type :del-obj :page-id page-id :id (:id %))
|
uchanges (mapv #(array-map :type :del-obj :page-id page-id :id (:id %))
|
||||||
|
|
|
@ -156,3 +156,34 @@
|
||||||
:xmlnsXlink "http://www.w3.org/1999/xlink"
|
:xmlnsXlink "http://www.w3.org/1999/xlink"
|
||||||
:xmlns "http://www.w3.org/2000/svg"}
|
:xmlns "http://www.w3.org/2000/svg"}
|
||||||
[:& wrapper {:shape frame :view-box vbox}]]))
|
[:& wrapper {:shape frame :view-box vbox}]]))
|
||||||
|
|
||||||
|
(mf/defc component-svg
|
||||||
|
{::mf/wrap [mf/memo]}
|
||||||
|
[{:keys [objects group zoom] :or {zoom 1} :as props}]
|
||||||
|
(let [modifier (-> (gpt/point (:x group) (:y group))
|
||||||
|
(gpt/negate)
|
||||||
|
(gmt/translate-matrix))
|
||||||
|
|
||||||
|
group-id (:id group)
|
||||||
|
|
||||||
|
modifier-ids (concat [group-id] (cph/get-children group-id objects))
|
||||||
|
update-fn #(assoc-in %1 [%2 :modifiers :displacement] modifier)
|
||||||
|
objects (reduce update-fn objects modifier-ids)
|
||||||
|
group (assoc-in group [:modifiers :displacement] modifier)
|
||||||
|
|
||||||
|
width (* (:width group) zoom)
|
||||||
|
height (* (:height group) zoom)
|
||||||
|
vbox (str "0 0 " (:width group 0)
|
||||||
|
" " (:height group 0))
|
||||||
|
wrapper (mf/use-memo
|
||||||
|
(mf/deps objects)
|
||||||
|
#(group-wrapper-factory objects))]
|
||||||
|
|
||||||
|
[:svg {:view-box vbox
|
||||||
|
:width width
|
||||||
|
:height height
|
||||||
|
:version "1.1"
|
||||||
|
:xmlnsXlink "http://www.w3.org/1999/xlink"
|
||||||
|
:xmlns "http://www.w3.org/2000/svg"}
|
||||||
|
[:& wrapper {:shape group :view-box vbox}]]))
|
||||||
|
|
||||||
|
|
|
@ -67,4 +67,4 @@
|
||||||
|
|
||||||
(defn ^:export dump-objects []
|
(defn ^:export dump-objects []
|
||||||
(let [page-id (get @state :current-page-id)]
|
(let [page-id (get @state :current-page-id)]
|
||||||
(logjs "state" (get-in @state [:workspace-data page-id :objects]))))
|
(logjs "state" (get-in @state [:workspace-data :pages-index page-id :objects]))))
|
||||||
|
|
|
@ -31,6 +31,7 @@
|
||||||
(def chat (icon-xref :chat))
|
(def chat (icon-xref :chat))
|
||||||
(def circle (icon-xref :circle))
|
(def circle (icon-xref :circle))
|
||||||
(def close (icon-xref :close))
|
(def close (icon-xref :close))
|
||||||
|
(def component (icon-xref :component))
|
||||||
(def copy (icon-xref :copy))
|
(def copy (icon-xref :copy))
|
||||||
(def curve (icon-xref :curve))
|
(def curve (icon-xref :curve))
|
||||||
(def download (icon-xref :download))
|
(def download (icon-xref :download))
|
||||||
|
|
|
@ -20,6 +20,7 @@
|
||||||
[app.main.ui.icons :as i]
|
[app.main.ui.icons :as i]
|
||||||
[app.util.dom :as dom]
|
[app.util.dom :as dom]
|
||||||
[app.main.data.workspace :as dw]
|
[app.main.data.workspace :as dw]
|
||||||
|
[app.main.data.workspace.libraries :as dwl]
|
||||||
[app.main.ui.hooks :refer [use-rxsub]]
|
[app.main.ui.hooks :refer [use-rxsub]]
|
||||||
[app.main.ui.components.dropdown :refer [dropdown]]))
|
[app.main.ui.components.dropdown :refer [dropdown]]))
|
||||||
|
|
||||||
|
@ -45,6 +46,7 @@
|
||||||
[{:keys [mdata] :as props}]
|
[{:keys [mdata] :as props}]
|
||||||
(let [{:keys [id] :as shape} (:shape mdata)
|
(let [{:keys [id] :as shape} (:shape mdata)
|
||||||
selected (:selected mdata)
|
selected (:selected mdata)
|
||||||
|
root-shape (:root-shape mdata)
|
||||||
|
|
||||||
do-duplicate #(st/emit! dw/duplicate-selected)
|
do-duplicate #(st/emit! dw/duplicate-selected)
|
||||||
do-delete #(st/emit! dw/delete-selected)
|
do-delete #(st/emit! dw/delete-selected)
|
||||||
|
@ -59,7 +61,15 @@
|
||||||
do-lock-shape #(st/emit! (dw/update-shape-flags id {:blocked true}))
|
do-lock-shape #(st/emit! (dw/update-shape-flags id {:blocked true}))
|
||||||
do-unlock-shape #(st/emit! (dw/update-shape-flags id {:blocked false}))
|
do-unlock-shape #(st/emit! (dw/update-shape-flags id {:blocked false}))
|
||||||
do-create-group #(st/emit! dw/group-selected)
|
do-create-group #(st/emit! dw/group-selected)
|
||||||
do-remove-group #(st/emit! dw/ungroup-selected)]
|
do-remove-group #(st/emit! dw/ungroup-selected)
|
||||||
|
do-add-component #(st/emit! dwl/add-component)
|
||||||
|
do-detach-component #(st/emit! (dwl/detach-component id))
|
||||||
|
do-reset-component #(st/emit! (dwl/reset-component id))
|
||||||
|
do-update-component #(do
|
||||||
|
(st/emit! (dwl/update-component id))
|
||||||
|
(st/emit! (dwl/sync-file {:file-id nil})))
|
||||||
|
do-navigate-component-file #(st/emit! (dwl/nav-to-component-file
|
||||||
|
(:component-file root-shape)))]
|
||||||
[:*
|
[:*
|
||||||
[:& menu-entry {:title "Copy"
|
[:& menu-entry {:title "Copy"
|
||||||
:shortcut "Ctrl + c"
|
:shortcut "Ctrl + c"
|
||||||
|
@ -101,13 +111,29 @@
|
||||||
[:& menu-entry {:title "Hide"
|
[:& menu-entry {:title "Hide"
|
||||||
:on-click do-hide-shape}])
|
:on-click do-hide-shape}])
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
(if (:blocked shape)
|
(if (:blocked shape)
|
||||||
[:& menu-entry {:title "Unlock"
|
[:& menu-entry {:title "Unlock"
|
||||||
:on-click do-unlock-shape}]
|
:on-click do-unlock-shape}]
|
||||||
[:& menu-entry {:title "Lock"
|
[:& menu-entry {:title "Lock"
|
||||||
:on-click do-lock-shape}])
|
:on-click do-lock-shape}])
|
||||||
|
|
||||||
|
[:& menu-separator]
|
||||||
|
|
||||||
|
(if (nil? (:shape-ref shape))
|
||||||
|
[:& menu-entry {:title "Create component"
|
||||||
|
:shortcut "Ctrl + K"
|
||||||
|
:on-click do-add-component}]
|
||||||
|
[:*
|
||||||
|
[:& menu-entry {:title "Detach instance"
|
||||||
|
:on-click do-detach-component}]
|
||||||
|
[:& menu-entry {:title "Reset overrides"
|
||||||
|
:on-click do-reset-component}]
|
||||||
|
(if (nil? (:component-file root-shape))
|
||||||
|
[:& menu-entry {:title "Update master component"
|
||||||
|
:on-click do-update-component}]
|
||||||
|
[:& menu-entry {:title "Go to master component file"
|
||||||
|
:on-click do-navigate-component-file}])])
|
||||||
|
|
||||||
[:& menu-separator]
|
[:& menu-separator]
|
||||||
[:& menu-entry {:title "Delete"
|
[:& menu-entry {:title "Delete"
|
||||||
:shortcut "Supr"
|
:shortcut "Supr"
|
||||||
|
|
|
@ -34,10 +34,11 @@
|
||||||
(def resize-point-circle-radius 10)
|
(def resize-point-circle-radius 10)
|
||||||
(def resize-point-rect-size 8)
|
(def resize-point-rect-size 8)
|
||||||
(def resize-side-height 8)
|
(def resize-side-height 8)
|
||||||
(def selection-rect-color "#1FDEA7")
|
(def selection-rect-color-normal "#1FDEA7")
|
||||||
|
(def selection-rect-color-component "#00E0FF")
|
||||||
(def selection-rect-width 1)
|
(def selection-rect-width 1)
|
||||||
|
|
||||||
(mf/defc selection-rect [{:keys [transform rect zoom]}]
|
(mf/defc selection-rect [{:keys [transform rect zoom color]}]
|
||||||
(let [{:keys [x y width height]} rect]
|
(let [{:keys [x y width height]} rect]
|
||||||
[:rect.main
|
[:rect.main
|
||||||
{:x x
|
{:x x
|
||||||
|
@ -45,7 +46,7 @@
|
||||||
:width width
|
:width width
|
||||||
:height height
|
:height height
|
||||||
:transform transform
|
:transform transform
|
||||||
:style {:stroke selection-rect-color
|
:style {:stroke color
|
||||||
:stroke-width (/ selection-rect-width zoom)
|
:stroke-width (/ selection-rect-width zoom)
|
||||||
:fill "transparent"}}]))
|
:fill "transparent"}}]))
|
||||||
|
|
||||||
|
@ -125,7 +126,7 @@
|
||||||
:on-mouse-down on-rotate}]))
|
:on-mouse-down on-rotate}]))
|
||||||
|
|
||||||
(mf/defc resize-point-handler
|
(mf/defc resize-point-handler
|
||||||
[{:keys [cx cy zoom position on-resize transform rotation]}]
|
[{:keys [cx cy zoom position on-resize transform rotation color]}]
|
||||||
(let [{cx' :x cy' :y} (gpt/transform (gpt/point cx cy) transform)
|
(let [{cx' :x cy' :y} (gpt/transform (gpt/point cx cy) transform)
|
||||||
rot-square (case position
|
rot-square (case position
|
||||||
:top-left 0
|
:top-left 0
|
||||||
|
@ -139,7 +140,7 @@
|
||||||
:vectorEffect "non-scaling-stroke"
|
:vectorEffect "non-scaling-stroke"
|
||||||
}
|
}
|
||||||
:fill "#FFFFFF"
|
:fill "#FFFFFF"
|
||||||
:stroke "#1FDEA7"
|
:stroke color
|
||||||
:cx cx'
|
:cx cx'
|
||||||
:cy cy'}]
|
:cy cy'}]
|
||||||
|
|
||||||
|
@ -173,6 +174,7 @@
|
||||||
[props]
|
[props]
|
||||||
(let [shape (obj/get props "shape")
|
(let [shape (obj/get props "shape")
|
||||||
zoom (obj/get props "zoom")
|
zoom (obj/get props "zoom")
|
||||||
|
color (obj/get props "color")
|
||||||
on-resize (obj/get props "on-resize")
|
on-resize (obj/get props "on-resize")
|
||||||
on-rotate (obj/get props "on-rotate")
|
on-rotate (obj/get props "on-rotate")
|
||||||
current-transform (mf/deref refs/current-transform)
|
current-transform (mf/deref refs/current-transform)
|
||||||
|
@ -186,8 +188,10 @@
|
||||||
;; Selection rect
|
;; Selection rect
|
||||||
[:& selection-rect {:rect selrect
|
[:& selection-rect {:rect selrect
|
||||||
:transform transform
|
:transform transform
|
||||||
:zoom zoom}]
|
:zoom zoom
|
||||||
[:& outline {:shape (geom/transform-shape shape)}]
|
:color color}]
|
||||||
|
[:& outline {:shape (geom/transform-shape shape)
|
||||||
|
:color color}]
|
||||||
|
|
||||||
;; Handlers
|
;; Handlers
|
||||||
(for [{:keys [type position props]} (handlers-for-selection selrect)]
|
(for [{:keys [type position props]} (handlers-for-selection selrect)]
|
||||||
|
@ -197,7 +201,8 @@
|
||||||
:on-rotate on-rotate
|
:on-rotate on-rotate
|
||||||
:on-resize (partial on-resize position)
|
:on-resize (partial on-resize position)
|
||||||
:transform transform
|
:transform transform
|
||||||
:rotation (:rotation shape)}
|
:rotation (:rotation shape)
|
||||||
|
:color color}
|
||||||
props (map->obj (merge common-props props))]
|
props (map->obj (merge common-props props))]
|
||||||
(case type
|
(case type
|
||||||
:rotation (when (not= :frame (:type shape)) [:> rotation-handler props])
|
:rotation (when (not= :frame (:type shape)) [:> rotation-handler props])
|
||||||
|
@ -206,7 +211,7 @@
|
||||||
|
|
||||||
;; --- Selection Handlers (Component)
|
;; --- Selection Handlers (Component)
|
||||||
(mf/defc path-edition-selection-handlers
|
(mf/defc path-edition-selection-handlers
|
||||||
[{:keys [shape modifiers zoom] :as props}]
|
[{:keys [shape modifiers zoom color] :as props}]
|
||||||
(letfn [(on-mouse-down [event index]
|
(letfn [(on-mouse-down [event index]
|
||||||
(dom/stop-propagation event)
|
(dom/stop-propagation event)
|
||||||
;; TODO: this need code ux refactor
|
;; TODO: this need code ux refactor
|
||||||
|
@ -240,26 +245,26 @@
|
||||||
:key index
|
:key index
|
||||||
:on-mouse-down #(on-mouse-down % index)
|
:on-mouse-down #(on-mouse-down % index)
|
||||||
:fill "#ffffff"
|
:fill "#ffffff"
|
||||||
:stroke "#1FDEA7"
|
:stroke color
|
||||||
:style {:cursor cur/move-pointer}}]))])))
|
:style {:cursor cur/move-pointer}}]))])))
|
||||||
|
|
||||||
;; TODO: add specs for clarity
|
;; TODO: add specs for clarity
|
||||||
|
|
||||||
(mf/defc text-edition-selection-handlers
|
(mf/defc text-edition-selection-handlers
|
||||||
[{:keys [shape zoom] :as props}]
|
[{:keys [shape zoom color] :as props}]
|
||||||
(let [{:keys [x y width height]} shape]
|
(let [{:keys [x y width height]} shape]
|
||||||
[:g.controls
|
[:g.controls
|
||||||
[:rect.main {:x x :y y
|
[:rect.main {:x x :y y
|
||||||
:transform (geom/transform-matrix shape)
|
:transform (geom/transform-matrix shape)
|
||||||
:width width
|
:width width
|
||||||
:height height
|
:height height
|
||||||
:style {:stroke "#1FDEA7"
|
:style {:stroke color
|
||||||
:stroke-width "0.5"
|
:stroke-width "0.5"
|
||||||
:stroke-opacity "1"
|
:stroke-opacity "1"
|
||||||
:fill "transparent"}}]]))
|
:fill "transparent"}}]]))
|
||||||
|
|
||||||
(mf/defc multiple-selection-handlers
|
(mf/defc multiple-selection-handlers
|
||||||
[{:keys [shapes selected zoom] :as props}]
|
[{:keys [shapes selected zoom color] :as props}]
|
||||||
(let [shape (geom/selection-rect shapes)
|
(let [shape (geom/selection-rect shapes)
|
||||||
shape-center (geom/center shape)
|
shape-center (geom/center shape)
|
||||||
on-resize (fn [current-position initial-position event]
|
on-resize (fn [current-position initial-position event]
|
||||||
|
@ -272,13 +277,14 @@
|
||||||
[:*
|
[:*
|
||||||
[:& controls {:shape shape
|
[:& controls {:shape shape
|
||||||
:zoom zoom
|
:zoom zoom
|
||||||
|
:color color
|
||||||
:on-resize on-resize
|
:on-resize on-resize
|
||||||
:on-rotate on-rotate}]
|
:on-rotate on-rotate}]
|
||||||
(when (debug? :selection-center)
|
(when (debug? :selection-center)
|
||||||
[:circle {:cx (:x shape-center) :cy (:y shape-center) :r 5 :fill "yellow"}])]))
|
[:circle {:cx (:x shape-center) :cy (:y shape-center) :r 5 :fill "yellow"}])]))
|
||||||
|
|
||||||
(mf/defc single-selection-handlers
|
(mf/defc single-selection-handlers
|
||||||
[{:keys [shape zoom] :as props}]
|
[{:keys [shape zoom color] :as props}]
|
||||||
(let [shape-id (:id shape)
|
(let [shape-id (:id shape)
|
||||||
shape (geom/transform-shape shape)
|
shape (geom/transform-shape shape)
|
||||||
shape' (if (debug? :simple-selection) (geom/selection-rect [shape]) shape)
|
shape' (if (debug? :simple-selection) (geom/selection-rect [shape]) shape)
|
||||||
|
@ -293,6 +299,7 @@
|
||||||
[:*
|
[:*
|
||||||
[:& controls {:shape shape'
|
[:& controls {:shape shape'
|
||||||
:zoom zoom
|
:zoom zoom
|
||||||
|
:color color
|
||||||
:on-rotate on-rotate
|
:on-rotate on-rotate
|
||||||
:on-resize on-resize}]]))
|
:on-resize on-resize}]]))
|
||||||
|
|
||||||
|
@ -304,7 +311,11 @@
|
||||||
shapes (->> (mf/deref (refs/objects-by-id selected))
|
shapes (->> (mf/deref (refs/objects-by-id selected))
|
||||||
(remove nil?))
|
(remove nil?))
|
||||||
num (count shapes)
|
num (count shapes)
|
||||||
{:keys [id type] :as shape} (first shapes)]
|
{:keys [id type] :as shape} (first shapes)
|
||||||
|
|
||||||
|
color (if (or (> num 1) (nil? (:shape-ref shape)))
|
||||||
|
selection-rect-color-normal
|
||||||
|
selection-rect-color-component)]
|
||||||
(cond
|
(cond
|
||||||
(zero? num)
|
(zero? num)
|
||||||
nil
|
nil
|
||||||
|
@ -312,18 +323,22 @@
|
||||||
(> num 1)
|
(> num 1)
|
||||||
[:& multiple-selection-handlers {:shapes shapes
|
[:& multiple-selection-handlers {:shapes shapes
|
||||||
:selected selected
|
:selected selected
|
||||||
:zoom zoom}]
|
:zoom zoom
|
||||||
|
:color color}]
|
||||||
|
|
||||||
(and (= type :text)
|
(and (= type :text)
|
||||||
(= edition (:id shape)))
|
(= edition (:id shape)))
|
||||||
[:& text-edition-selection-handlers {:shape shape
|
[:& text-edition-selection-handlers {:shape shape
|
||||||
:zoom zoom}]
|
:zoom zoom
|
||||||
|
:color color}]
|
||||||
(and (or (= type :path)
|
(and (or (= type :path)
|
||||||
(= type :curve))
|
(= type :curve))
|
||||||
(= edition (:id shape)))
|
(= edition (:id shape)))
|
||||||
[:& path-edition-selection-handlers {:shape shape
|
[:& path-edition-selection-handlers {:shape shape
|
||||||
:zoom zoom}]
|
:zoom zoom
|
||||||
|
:color color}]
|
||||||
|
|
||||||
:else
|
:else
|
||||||
[:& single-selection-handlers {:shape shape
|
[:& single-selection-handlers {:shape shape
|
||||||
:zoom zoom}])))
|
:zoom zoom
|
||||||
|
:color color}])))
|
||||||
|
|
|
@ -158,7 +158,8 @@
|
||||||
:zoom zoom}]
|
:zoom zoom}]
|
||||||
|
|
||||||
(when dest-shape
|
(when dest-shape
|
||||||
[:& outline {:shape dest-shape}])])))
|
[:& outline {:shape dest-shape
|
||||||
|
:color "#31EFB8"}])])))
|
||||||
|
|
||||||
|
|
||||||
(mf/defc interaction-handle
|
(mf/defc interaction-handle
|
||||||
|
|
|
@ -22,6 +22,7 @@
|
||||||
[props]
|
[props]
|
||||||
(let [zoom (mf/deref refs/selected-zoom)
|
(let [zoom (mf/deref refs/selected-zoom)
|
||||||
shape (unchecked-get props "shape")
|
shape (unchecked-get props "shape")
|
||||||
|
color (unchecked-get props "color")
|
||||||
transform (gsh/transform-matrix shape)
|
transform (gsh/transform-matrix shape)
|
||||||
{:keys [id x y width height]} shape
|
{:keys [id x y width height]} shape
|
||||||
|
|
||||||
|
@ -31,7 +32,7 @@
|
||||||
"rect")
|
"rect")
|
||||||
|
|
||||||
common {:fill "transparent"
|
common {:fill "transparent"
|
||||||
:stroke "#31EFB8"
|
:stroke color
|
||||||
:strokeWidth (/ 1 zoom)
|
:strokeWidth (/ 1 zoom)
|
||||||
:pointerEvents "none"
|
:pointerEvents "none"
|
||||||
:transform transform}
|
:transform transform}
|
||||||
|
@ -42,10 +43,10 @@
|
||||||
:cy (+ y (/ height 2))
|
:cy (+ y (/ height 2))
|
||||||
:rx (/ width 2)
|
:rx (/ width 2)
|
||||||
:ry (/ height 2)}
|
:ry (/ height 2)}
|
||||||
|
|
||||||
(:curve :path)
|
(:curve :path)
|
||||||
{:d (path/render-path shape)}
|
{:d (path/render-path shape)}
|
||||||
|
|
||||||
{:x x
|
{:x x
|
||||||
:y y
|
:y y
|
||||||
:width width
|
:width width
|
||||||
|
|
|
@ -14,6 +14,7 @@
|
||||||
[app.common.geom.shapes :as geom]
|
[app.common.geom.shapes :as geom]
|
||||||
[app.common.media :as cm]
|
[app.common.media :as cm]
|
||||||
[app.common.pages :as cp]
|
[app.common.pages :as cp]
|
||||||
|
[app.common.pages-helpers :as cph]
|
||||||
[app.common.uuid :as uuid]
|
[app.common.uuid :as uuid]
|
||||||
[app.config :as cfg]
|
[app.config :as cfg]
|
||||||
[app.main.data.workspace :as dw]
|
[app.main.data.workspace :as dw]
|
||||||
|
@ -21,6 +22,7 @@
|
||||||
[app.main.data.colors :as dc]
|
[app.main.data.colors :as dc]
|
||||||
[app.main.refs :as refs]
|
[app.main.refs :as refs]
|
||||||
[app.main.store :as st]
|
[app.main.store :as st]
|
||||||
|
[app.main.exports :as exports]
|
||||||
[app.main.ui.components.context-menu :refer [context-menu]]
|
[app.main.ui.components.context-menu :refer [context-menu]]
|
||||||
[app.main.ui.components.file-uploader :refer [file-uploader]]
|
[app.main.ui.components.file-uploader :refer [file-uploader]]
|
||||||
[app.main.ui.components.tab-container :refer [tab-container tab-element]]
|
[app.main.ui.components.tab-container :refer [tab-container tab-element]]
|
||||||
|
@ -38,6 +40,63 @@
|
||||||
[okulary.core :as l]
|
[okulary.core :as l]
|
||||||
[rumext.alpha :as mf]))
|
[rumext.alpha :as mf]))
|
||||||
|
|
||||||
|
(mf/defc components-box
|
||||||
|
[{:keys [file-id local? components] :as props}]
|
||||||
|
(let [state (mf/use-state {:menu-open false
|
||||||
|
:top nil
|
||||||
|
:left nil
|
||||||
|
:component-id nil})
|
||||||
|
on-delete
|
||||||
|
(mf/use-callback
|
||||||
|
(mf/deps state)
|
||||||
|
(fn []
|
||||||
|
(st/emit! (dwl/delete-component {:id (:component-id @state)}))
|
||||||
|
(st/emit! (dwl/sync-file {:file-id nil}))))
|
||||||
|
|
||||||
|
on-context-menu
|
||||||
|
(mf/use-callback
|
||||||
|
(fn [component-id]
|
||||||
|
(fn [event]
|
||||||
|
(when local?
|
||||||
|
(let [pos (dom/get-client-position event)
|
||||||
|
top (:y pos)
|
||||||
|
left (- (:x pos) 20)]
|
||||||
|
(dom/prevent-default event)
|
||||||
|
(swap! state assoc :menu-open true
|
||||||
|
:top top
|
||||||
|
:left left
|
||||||
|
:component-id component-id))))))
|
||||||
|
|
||||||
|
on-drag-start
|
||||||
|
(mf/use-callback
|
||||||
|
(fn [component-id event]
|
||||||
|
(dnd/set-data! event "app/component" {:file-id (if local? nil file-id)
|
||||||
|
:component-id component-id})
|
||||||
|
(dnd/set-allowed-effect! event "move")))]
|
||||||
|
|
||||||
|
[:div.asset-group
|
||||||
|
[:div.group-title
|
||||||
|
(tr "workspace.assets.components")
|
||||||
|
[:span (str "\u00A0(") (count components) ")"]] ;; Unicode 00A0 is non-breaking space
|
||||||
|
[:div.group-grid.big
|
||||||
|
(for [component components]
|
||||||
|
[:div.grid-cell {:key (:id component)
|
||||||
|
:draggable true
|
||||||
|
:on-context-menu (on-context-menu (:id component))
|
||||||
|
:on-drag-start (partial on-drag-start (:id component))}
|
||||||
|
[:& exports/component-svg {:group (get-in component [:objects (:id component)])
|
||||||
|
:objects (:objects component)}]
|
||||||
|
[:div.cell-name (:name component)]])
|
||||||
|
|
||||||
|
(when local?
|
||||||
|
[:& context-menu
|
||||||
|
{:selectable false
|
||||||
|
:show (:menu-open @state)
|
||||||
|
:on-close #(swap! state assoc :menu-open false)
|
||||||
|
:top (:top @state)
|
||||||
|
:left (:left @state)
|
||||||
|
:options [[(tr "workspace.assets.delete") on-delete]]}])]]))
|
||||||
|
|
||||||
(mf/defc graphics-box
|
(mf/defc graphics-box
|
||||||
[{:keys [file-id local? objects open? on-open on-close] :as props}]
|
[{:keys [file-id local? objects open? on-open on-close] :as props}]
|
||||||
(let [input-ref (mf/use-ref nil)
|
(let [input-ref (mf/use-ref nil)
|
||||||
|
@ -126,7 +185,6 @@
|
||||||
:left (:left @state)
|
:left (:left @state)
|
||||||
:options [[(tr "workspace.assets.delete") on-delete]]}])])]))
|
:options [[(tr "workspace.assets.delete") on-delete]]}])])]))
|
||||||
|
|
||||||
|
|
||||||
(mf/defc color-item
|
(mf/defc color-item
|
||||||
[{:keys [color local? locale file-id] :as props}]
|
[{:keys [color local? locale file-id] :as props}]
|
||||||
(let [rename? (= (:color-for-rename @refs/workspace-local) (:id color))
|
(let [rename? (= (:color-for-rename @refs/workspace-local) (:id color))
|
||||||
|
@ -287,32 +345,45 @@
|
||||||
(vals (get-in state [:workspace-libraries id :data :media])))))
|
(vals (get-in state [:workspace-libraries id :data :media])))))
|
||||||
st/state =))
|
st/state =))
|
||||||
|
|
||||||
|
(defn file-components-ref
|
||||||
|
[id]
|
||||||
|
(l/derived (fn [state]
|
||||||
|
(let [wfile (:workspace-file state)]
|
||||||
|
(if (= (:id wfile) id)
|
||||||
|
(vals (get-in wfile [:data :components]))
|
||||||
|
(vals (get-in state [:workspace-libraries id :data :components])))))
|
||||||
|
st/state =))
|
||||||
|
|
||||||
(defn apply-filters
|
(defn apply-filters
|
||||||
[coll filters]
|
[coll filters]
|
||||||
(filter (fn [item]
|
(->> coll
|
||||||
(or (matches-search (:name item "!$!") (:term filters))
|
(filter (fn [item]
|
||||||
(matches-search (:value item "!$!") (:term filters))))
|
(or (matches-search (:name item "!$!") (:term filters))
|
||||||
coll))
|
(matches-search (:value item "!$!") (:term filters)))))
|
||||||
|
(sort-by #(str/lower (:name %)))))
|
||||||
|
|
||||||
(mf/defc file-library
|
(mf/defc file-library
|
||||||
[{:keys [file local? open? filters locale] :as props}]
|
[{:keys [file local? open? filters locale] :as props}]
|
||||||
(let [open? (mf/use-state open?)
|
(let [open? (mf/use-state open?)
|
||||||
shared? (:is-shared file)
|
shared? (:is-shared file)
|
||||||
router (mf/deref refs/router)
|
router (mf/deref refs/router)
|
||||||
toggle-open #(swap! open? not)
|
toggle-open #(swap! open? not)
|
||||||
|
|
||||||
toggles (mf/use-state #{:graphics :colors})
|
toggles (mf/use-state #{:graphics :colors})
|
||||||
|
|
||||||
url (rt/resolve router :workspace
|
url (rt/resolve router :workspace
|
||||||
{:project-id (:project-id file)
|
{:project-id (:project-id file)
|
||||||
:file-id (:id file)}
|
:file-id (:id file)}
|
||||||
{:page-id (get-in file [:data :pages 0])})
|
{:page-id (get-in file [:data :pages 0])})
|
||||||
|
|
||||||
colors-ref (mf/use-memo (mf/deps (:id file)) #(file-colors-ref (:id file)))
|
colors-ref (mf/use-memo (mf/deps (:id file)) #(file-colors-ref (:id file)))
|
||||||
colors (apply-filters (mf/deref colors-ref) filters)
|
colors (apply-filters (mf/deref colors-ref) filters)
|
||||||
|
|
||||||
media-ref (mf/use-memo (mf/deps (:id file)) #(file-media-ref (:id file)))
|
media-ref (mf/use-memo (mf/deps (:id file)) #(file-media-ref (:id file)))
|
||||||
media (apply-filters (mf/deref media-ref) filters)]
|
media (apply-filters (mf/deref media-ref) filters)
|
||||||
|
|
||||||
|
components-ref (mf/use-memo (mf/deps (:id file)) #(file-components-ref (:id file)))
|
||||||
|
components (apply-filters (mf/deref components-ref) filters)]
|
||||||
|
|
||||||
[:div.tool-window
|
[:div.tool-window
|
||||||
[:div.tool-window-bar
|
[:div.tool-window-bar
|
||||||
|
@ -332,15 +403,23 @@
|
||||||
[:a {:href (str "#" url) :target "_blank"} i/chain]]])]
|
[:a {:href (str "#" url) :target "_blank"} i/chain]]])]
|
||||||
|
|
||||||
(when @open?
|
(when @open?
|
||||||
(let [show-graphics? (and (or (= (:box filters) :all)
|
(let [show-components? (and (or (= (:box filters) :all)
|
||||||
(= (:box filters) :graphics))
|
(= (:box filters) :components))
|
||||||
(or (> (count media) 0)
|
(or (> (count components) 0)
|
||||||
(str/empty? (:term filters))))
|
(str/empty? (:term filters))))
|
||||||
show-colors? (and (or (= (:box filters) :all)
|
show-graphics? (and (or (= (:box filters) :all)
|
||||||
(= (:box filters) :colors))
|
(= (:box filters) :graphics))
|
||||||
(or (> (count colors) 0)
|
(or (> (count media) 0)
|
||||||
(str/empty? (:term filters))))]
|
(str/empty? (:term filters))))
|
||||||
|
show-colors? (and (or (= (:box filters) :all)
|
||||||
|
(= (:box filters) :colors))
|
||||||
|
(or (> (count colors) 0)
|
||||||
|
(str/empty? (:term filters))))]
|
||||||
[:div.tool-window-content
|
[:div.tool-window-content
|
||||||
|
(when show-components?
|
||||||
|
[:& components-box {:file-id (:id file)
|
||||||
|
:local? local?
|
||||||
|
:components components}])
|
||||||
(when show-graphics?
|
(when show-graphics?
|
||||||
[:& graphics-box {:file-id (:id file)
|
[:& graphics-box {:file-id (:id file)
|
||||||
:local? local?
|
:local? local?
|
||||||
|
@ -357,10 +436,11 @@
|
||||||
:on-open #(swap! toggles conj :colors)
|
:on-open #(swap! toggles conj :colors)
|
||||||
:on-close #(swap! toggles disj :colors)}])
|
:on-close #(swap! toggles disj :colors)}])
|
||||||
|
|
||||||
(when (and (not show-graphics?) (not show-colors?))
|
(when (and (not show-components?) (not show-graphics?) (not show-colors?))
|
||||||
[:div.asset-group
|
[:div.asset-group
|
||||||
[:div.group-title (t locale "workspace.assets.not-found")]])]))]))
|
[:div.group-title (t locale "workspace.assets.not-found")]])]))]))
|
||||||
|
|
||||||
|
|
||||||
(mf/defc assets-toolbox
|
(mf/defc assets-toolbox
|
||||||
[{:keys [team-id file] :as props}]
|
[{:keys [team-id file] :as props}]
|
||||||
(let [libraries (mf/deref refs/workspace-libraries)
|
(let [libraries (mf/deref refs/workspace-libraries)
|
||||||
|
|
|
@ -43,7 +43,9 @@
|
||||||
:rect i/box
|
:rect i/box
|
||||||
:curve i/curve
|
:curve i/curve
|
||||||
:text i/text
|
:text i/text
|
||||||
:group i/folder
|
:group (if (nil? (:component-id shape))
|
||||||
|
i/folder
|
||||||
|
i/component)
|
||||||
nil))
|
nil))
|
||||||
|
|
||||||
;; --- Layer Name
|
;; --- Layer Name
|
||||||
|
@ -186,6 +188,7 @@
|
||||||
[:li {:on-context-menu on-context-menu
|
[:li {:on-context-menu on-context-menu
|
||||||
:ref dref
|
:ref dref
|
||||||
:class (dom/classnames
|
:class (dom/classnames
|
||||||
|
:component (not (nil? (:component-id item)))
|
||||||
:dnd-over (= (:over dprops) :center)
|
:dnd-over (= (:over dprops) :center)
|
||||||
:dnd-over-top (= (:over dprops) :top)
|
:dnd-over-top (= (:over dprops) :top)
|
||||||
:dnd-over-bot (= (:over dprops) :bot)
|
:dnd-over-bot (= (:over dprops) :bot)
|
||||||
|
@ -285,7 +288,18 @@
|
||||||
|
|
||||||
(defn- strip-objects
|
(defn- strip-objects
|
||||||
[objects]
|
[objects]
|
||||||
(let [strip-data #(select-keys % [:id :name :blocked :hidden :shapes :type :content :parent-id :metadata])]
|
(let [strip-data #(select-keys % [:id
|
||||||
|
:name
|
||||||
|
:blocked
|
||||||
|
:hidden
|
||||||
|
:shapes
|
||||||
|
:type
|
||||||
|
:content
|
||||||
|
:parent-id
|
||||||
|
:component-id
|
||||||
|
:component-file
|
||||||
|
:shape-ref
|
||||||
|
:metadata])]
|
||||||
(persistent!
|
(persistent!
|
||||||
(reduce-kv (fn [res id obj]
|
(reduce-kv (fn [res id obj]
|
||||||
(assoc! res id (strip-data obj)))
|
(assoc! res id (strip-data obj)))
|
||||||
|
|
|
@ -22,6 +22,7 @@
|
||||||
[app.common.data :as d]
|
[app.common.data :as d]
|
||||||
[app.main.constants :as c]
|
[app.main.constants :as c]
|
||||||
[app.main.data.workspace :as dw]
|
[app.main.data.workspace :as dw]
|
||||||
|
[app.main.data.workspace.libraries :as dwl]
|
||||||
[app.main.data.workspace.drawing :as dd]
|
[app.main.data.workspace.drawing :as dd]
|
||||||
[app.main.data.colors :as dwc]
|
[app.main.data.colors :as dwc]
|
||||||
[app.main.data.fetch :as mdf]
|
[app.main.data.fetch :as mdf]
|
||||||
|
@ -132,12 +133,16 @@
|
||||||
hover (or (unchecked-get props "hover") #{})
|
hover (or (unchecked-get props "hover") #{})
|
||||||
outline? (set/union selected hover)
|
outline? (set/union selected hover)
|
||||||
shapes (->> (vals objects) (filter (comp outline? :id)))
|
shapes (->> (vals objects) (filter (comp outline? :id)))
|
||||||
transform (mf/deref refs/current-transform)]
|
transform (mf/deref refs/current-transform)
|
||||||
|
color (if (or (> (count shapes) 1) (nil? (:shape-ref (first shapes))))
|
||||||
|
"#31EFB8"
|
||||||
|
"#00E0FF")]
|
||||||
(when (nil? transform)
|
(when (nil? transform)
|
||||||
[:g.outlines
|
[:g.outlines
|
||||||
(for [shape shapes]
|
(for [shape shapes]
|
||||||
[:& outline {:key (str "outline-" (:id shape))
|
[:& outline {:key (str "outline-" (:id shape))
|
||||||
:shape (gsh/transform-shape shape)}])])))
|
:shape (gsh/transform-shape shape)
|
||||||
|
:color color}])])))
|
||||||
|
|
||||||
(mf/defc frames
|
(mf/defc frames
|
||||||
{::mf/wrap [mf/memo]
|
{::mf/wrap [mf/memo]
|
||||||
|
@ -454,6 +459,7 @@
|
||||||
on-drag-enter
|
on-drag-enter
|
||||||
(fn [e]
|
(fn [e]
|
||||||
(when (or (dnd/has-type? e "app/shape")
|
(when (or (dnd/has-type? e "app/shape")
|
||||||
|
(dnd/has-type? e "app/component")
|
||||||
(dnd/has-type? e "Files")
|
(dnd/has-type? e "Files")
|
||||||
(dnd/has-type? e "text/uri-list"))
|
(dnd/has-type? e "text/uri-list"))
|
||||||
(dom/prevent-default e)))
|
(dom/prevent-default e)))
|
||||||
|
@ -461,6 +467,7 @@
|
||||||
on-drag-over
|
on-drag-over
|
||||||
(fn [e]
|
(fn [e]
|
||||||
(when (or (dnd/has-type? e "app/shape")
|
(when (or (dnd/has-type? e "app/shape")
|
||||||
|
(dnd/has-type? e "app/component")
|
||||||
(dnd/has-type? e "Files")
|
(dnd/has-type? e "Files")
|
||||||
(dnd/has-type? e "text/uri-list"))
|
(dnd/has-type? e "text/uri-list"))
|
||||||
(dom/prevent-default e)))
|
(dom/prevent-default e)))
|
||||||
|
@ -491,6 +498,10 @@
|
||||||
(assoc :x final-x)
|
(assoc :x final-x)
|
||||||
(assoc :y final-y)))))
|
(assoc :y final-y)))))
|
||||||
|
|
||||||
|
(dnd/has-type? event "app/component")
|
||||||
|
(let [{:keys [component-id file-id]} (dnd/get-data event "app/component")]
|
||||||
|
(st/emit! (dwl/instantiate-component file-id component-id)))
|
||||||
|
|
||||||
(dnd/has-type? event "text/uri-list")
|
(dnd/has-type? event "text/uri-list")
|
||||||
(let [data (dnd/get-data event "text/uri-list")
|
(let [data (dnd/get-data event "text/uri-list")
|
||||||
lines (str/lines data)
|
lines (str/lines data)
|
||||||
|
|
|
@ -16,6 +16,7 @@
|
||||||
[potok.core :as ptk]
|
[potok.core :as ptk]
|
||||||
[reitit.core :as r]
|
[reitit.core :as r]
|
||||||
[app.common.data :as d]
|
[app.common.data :as d]
|
||||||
|
[app.config :as cfg]
|
||||||
[app.util.browser-history :as bhistory]
|
[app.util.browser-history :as bhistory]
|
||||||
[app.util.timers :as ts])
|
[app.util.timers :as ts])
|
||||||
(:import
|
(:import
|
||||||
|
@ -112,6 +113,19 @@
|
||||||
|
|
||||||
(def navigate nav)
|
(def navigate nav)
|
||||||
|
|
||||||
|
(deftype NavigateNewWindow [id params qparams]
|
||||||
|
ptk/EffectEvent
|
||||||
|
(effect [_ state stream]
|
||||||
|
(let [router (:router state)
|
||||||
|
path (resolve router id params qparams)
|
||||||
|
uri (str cfg/public-uri "/#" path)]
|
||||||
|
(js/window.open uri "_blank"))))
|
||||||
|
|
||||||
|
(defn nav-new-window
|
||||||
|
([id] (nav-new-window id nil nil))
|
||||||
|
([id params] (nav-new-window id params nil))
|
||||||
|
([id params qparams] (NavigateNewWindow. id params qparams)))
|
||||||
|
|
||||||
;; --- History API
|
;; --- History API
|
||||||
|
|
||||||
(defn initialize-history
|
(defn initialize-history
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue