mirror of
https://github.com/penpot/penpot.git
synced 2025-05-18 14:06:09 +02:00
♻️ Refactor workspace common
This commit is contained in:
parent
7406aac0c7
commit
b38ffdcf30
25 changed files with 492 additions and 449 deletions
|
@ -1,3 +1,9 @@
|
||||||
|
;; This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
;; License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
;; file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
;;
|
||||||
|
;; Copyright (c) UXBOX Labs SL
|
||||||
|
|
||||||
(ns app.common.geom.shapes.bounds
|
(ns app.common.geom.shapes.bounds
|
||||||
(:require
|
(:require
|
||||||
[app.common.data :as d]
|
[app.common.data :as d]
|
||||||
|
|
|
@ -455,7 +455,7 @@
|
||||||
(->> users
|
(->> users
|
||||||
(d/index-by :id)
|
(d/index-by :id)
|
||||||
(assoc state :file-comments-users)))]
|
(assoc state :file-comments-users)))]
|
||||||
(ptk/reify ::fetch-team-users
|
(ptk/reify ::fetch-file-comments-users
|
||||||
ptk/WatchEvent
|
ptk/WatchEvent
|
||||||
(watch [_ state _]
|
(watch [_ state _]
|
||||||
(let [share-id (-> state :viewer-local :share-id)]
|
(let [share-id (-> state :viewer-local :share-id)]
|
||||||
|
|
|
@ -28,8 +28,10 @@
|
||||||
[app.main.data.users :as du]
|
[app.main.data.users :as du]
|
||||||
[app.main.data.workspace.bool :as dwb]
|
[app.main.data.workspace.bool :as dwb]
|
||||||
[app.main.data.workspace.changes :as dch]
|
[app.main.data.workspace.changes :as dch]
|
||||||
|
[app.main.data.workspace.collapse :as dwco]
|
||||||
[app.main.data.workspace.common :as dwc]
|
[app.main.data.workspace.common :as dwc]
|
||||||
[app.main.data.workspace.drawing :as dwd]
|
[app.main.data.workspace.drawing :as dwd]
|
||||||
|
[app.main.data.workspace.edition :as dwe]
|
||||||
[app.main.data.workspace.fix-bool-contents :as fbc]
|
[app.main.data.workspace.fix-bool-contents :as fbc]
|
||||||
[app.main.data.workspace.groups :as dwg]
|
[app.main.data.workspace.groups :as dwg]
|
||||||
[app.main.data.workspace.guides :as dwgu]
|
[app.main.data.workspace.guides :as dwgu]
|
||||||
|
@ -43,6 +45,7 @@
|
||||||
[app.main.data.workspace.path.shapes-to-path :as dwps]
|
[app.main.data.workspace.path.shapes-to-path :as dwps]
|
||||||
[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.shapes :as dwsh]
|
||||||
[app.main.data.workspace.state-helpers :as wsh]
|
[app.main.data.workspace.state-helpers :as wsh]
|
||||||
[app.main.data.workspace.thumbnails :as dwth]
|
[app.main.data.workspace.thumbnails :as dwth]
|
||||||
[app.main.data.workspace.transforms :as dwt]
|
[app.main.data.workspace.transforms :as dwt]
|
||||||
|
@ -54,6 +57,7 @@
|
||||||
[app.util.globals :as ug]
|
[app.util.globals :as ug]
|
||||||
[app.util.http :as http]
|
[app.util.http :as http]
|
||||||
[app.util.i18n :as i18n]
|
[app.util.i18n :as i18n]
|
||||||
|
[app.util.names :as un]
|
||||||
[app.util.router :as rt]
|
[app.util.router :as rt]
|
||||||
[app.util.timers :as tm]
|
[app.util.timers :as tm]
|
||||||
[app.util.webapi :as wapi]
|
[app.util.webapi :as wapi]
|
||||||
|
@ -264,8 +268,8 @@
|
||||||
ptk/WatchEvent
|
ptk/WatchEvent
|
||||||
(watch [it state _]
|
(watch [it state _]
|
||||||
(let [pages (get-in state [:workspace-data :pages-index])
|
(let [pages (get-in state [:workspace-data :pages-index])
|
||||||
unames (dwc/retrieve-used-names pages)
|
unames (un/retrieve-used-names pages)
|
||||||
name (dwc/generate-unique-name unames "Page-1")
|
name (un/generate-unique-name unames "Page-1")
|
||||||
|
|
||||||
changes (-> (pcb/empty-changes it)
|
changes (-> (pcb/empty-changes it)
|
||||||
(pcb/add-empty-page id name))]
|
(pcb/add-empty-page id name))]
|
||||||
|
@ -279,9 +283,9 @@
|
||||||
(watch [it state _]
|
(watch [it state _]
|
||||||
(let [id (uuid/next)
|
(let [id (uuid/next)
|
||||||
pages (get-in state [:workspace-data :pages-index])
|
pages (get-in state [:workspace-data :pages-index])
|
||||||
unames (dwc/retrieve-used-names pages)
|
unames (un/retrieve-used-names pages)
|
||||||
page (get-in state [:workspace-data :pages-index page-id])
|
page (get-in state [:workspace-data :pages-index page-id])
|
||||||
name (dwc/generate-unique-name unames (:name page))
|
name (un/generate-unique-name unames (:name page))
|
||||||
|
|
||||||
no_thumbnails_objects (->> (:objects page)
|
no_thumbnails_objects (->> (:objects page)
|
||||||
(d/mapm (fn [_ val] (dissoc val :use-for-thumbnail?))))
|
(d/mapm (fn [_ val] (dissoc val :use-for-thumbnail?))))
|
||||||
|
@ -577,7 +581,7 @@
|
||||||
hover-guides (get-in state [:workspace-guides :hover])]
|
hover-guides (get-in state [:workspace-guides :hover])]
|
||||||
(cond
|
(cond
|
||||||
(d/not-empty? selected)
|
(d/not-empty? selected)
|
||||||
(rx/of (dwc/delete-shapes selected)
|
(rx/of (dwsh/delete-shapes selected)
|
||||||
(dws/deselect-all))
|
(dws/deselect-all))
|
||||||
|
|
||||||
(d/not-empty? hover-guides)
|
(d/not-empty? hover-guides)
|
||||||
|
@ -795,7 +799,7 @@
|
||||||
ids)]
|
ids)]
|
||||||
|
|
||||||
(rx/of (dch/commit-changes changes)
|
(rx/of (dch/commit-changes changes)
|
||||||
(dwc/expand-collapse parent-id))))))
|
(dwco/expand-collapse parent-id))))))
|
||||||
|
|
||||||
(defn relocate-selected-shapes
|
(defn relocate-selected-shapes
|
||||||
[parent-id to-index]
|
[parent-id to-index]
|
||||||
|
@ -820,15 +824,15 @@
|
||||||
|
|
||||||
(case type
|
(case type
|
||||||
:text
|
:text
|
||||||
(rx/of (dwc/start-edition-mode id))
|
(rx/of (dwe/start-edition-mode id))
|
||||||
|
|
||||||
(:group :bool)
|
(:group :bool)
|
||||||
(rx/of (dwc/select-shapes (into (d/ordered-set) [(last shapes)])))
|
(rx/of (dws/select-shapes (into (d/ordered-set) [(last shapes)])))
|
||||||
|
|
||||||
:svg-raw
|
:svg-raw
|
||||||
nil
|
nil
|
||||||
|
|
||||||
(rx/of (dwc/start-edition-mode id)
|
(rx/of (dwe/start-edition-mode id)
|
||||||
(dwdp/start-path-edit id)))))))))
|
(dwdp/start-path-edit id)))))))))
|
||||||
|
|
||||||
|
|
||||||
|
@ -1548,7 +1552,7 @@
|
||||||
(into (d/ordered-set)))]
|
(into (d/ordered-set)))]
|
||||||
|
|
||||||
(rx/of (dch/commit-changes changes)
|
(rx/of (dch/commit-changes changes)
|
||||||
(dwc/select-shapes selected))))]
|
(dws/select-shapes selected))))]
|
||||||
|
|
||||||
(ptk/reify ::paste-shape
|
(ptk/reify ::paste-shape
|
||||||
ptk/WatchEvent
|
ptk/WatchEvent
|
||||||
|
@ -1597,7 +1601,7 @@
|
||||||
:content (as-content text)})]
|
:content (as-content text)})]
|
||||||
(rx/of (dwu/start-undo-transaction)
|
(rx/of (dwu/start-undo-transaction)
|
||||||
(dws/deselect-all)
|
(dws/deselect-all)
|
||||||
(dwc/add-shape shape)
|
(dwsh/add-shape shape)
|
||||||
(dwu/commit-undo-transaction))))))
|
(dwu/commit-undo-transaction))))))
|
||||||
|
|
||||||
;; TODO: why not implement it in terms of upload-media-workspace?
|
;; TODO: why not implement it in terms of upload-media-workspace?
|
||||||
|
@ -1682,9 +1686,9 @@
|
||||||
(cp/setup-rect-selrect))]
|
(cp/setup-rect-selrect))]
|
||||||
(rx/of
|
(rx/of
|
||||||
(dwu/start-undo-transaction)
|
(dwu/start-undo-transaction)
|
||||||
(dwc/add-shape shape)
|
(dwsh/add-shape shape)
|
||||||
|
(dwsh/move-shapes-into-frame (:id shape) selected)
|
||||||
|
|
||||||
(dwc/move-shapes-into-frame (:id shape) selected)
|
|
||||||
(dwu/commit-undo-transaction))))))))
|
(dwu/commit-undo-transaction))))))))
|
||||||
|
|
||||||
|
|
||||||
|
@ -1707,10 +1711,10 @@
|
||||||
(dm/export dwly/set-opacity)
|
(dm/export dwly/set-opacity)
|
||||||
|
|
||||||
;; Common
|
;; Common
|
||||||
(dm/export dwc/add-shape)
|
(dm/export dwsh/add-shape)
|
||||||
(dm/export dwc/clear-edition-mode)
|
(dm/export dwe/clear-edition-mode)
|
||||||
(dm/export dwc/select-shapes)
|
(dm/export dws/select-shapes)
|
||||||
(dm/export dwc/start-edition-mode)
|
(dm/export dwe/start-edition-mode)
|
||||||
|
|
||||||
;; Drawing
|
;; Drawing
|
||||||
(dm/export dwd/select-for-drawing)
|
(dm/export dwd/select-for-drawing)
|
||||||
|
|
|
@ -13,8 +13,9 @@
|
||||||
[app.common.path.shapes-to-path :as stp]
|
[app.common.path.shapes-to-path :as stp]
|
||||||
[app.common.uuid :as uuid]
|
[app.common.uuid :as uuid]
|
||||||
[app.main.data.workspace.changes :as dch]
|
[app.main.data.workspace.changes :as dch]
|
||||||
[app.main.data.workspace.common :as dwc]
|
[app.main.data.workspace.selection :as dws]
|
||||||
[app.main.data.workspace.state-helpers :as wsh]
|
[app.main.data.workspace.state-helpers :as wsh]
|
||||||
|
[app.util.names :as un]
|
||||||
[beicon.core :as rx]
|
[beicon.core :as rx]
|
||||||
[cuerdas.core :as str]
|
[cuerdas.core :as str]
|
||||||
[potok.core :as ptk]))
|
[potok.core :as ptk]))
|
||||||
|
@ -89,8 +90,8 @@
|
||||||
(let [page-id (:current-page-id state)
|
(let [page-id (:current-page-id state)
|
||||||
objects (wsh/lookup-page-objects state)
|
objects (wsh/lookup-page-objects state)
|
||||||
base-name (-> bool-type d/name str/capital (str "-1"))
|
base-name (-> bool-type d/name str/capital (str "-1"))
|
||||||
name (-> (dwc/retrieve-used-names objects)
|
name (-> (un/retrieve-used-names objects)
|
||||||
(dwc/generate-unique-name base-name))
|
(un/generate-unique-name base-name))
|
||||||
shapes (selected-shapes state)]
|
shapes (selected-shapes state)]
|
||||||
|
|
||||||
(when-not (empty? shapes)
|
(when-not (empty? shapes)
|
||||||
|
@ -101,7 +102,7 @@
|
||||||
(pcb/add-object boolean-data {:index index})
|
(pcb/add-object boolean-data {:index index})
|
||||||
(pcb/change-parent shape-id shapes))]
|
(pcb/change-parent shape-id shapes))]
|
||||||
(rx/of (dch/commit-changes changes)
|
(rx/of (dch/commit-changes changes)
|
||||||
(dwc/select-shapes (d/ordered-set shape-id)))))))))
|
(dws/select-shapes (d/ordered-set shape-id)))))))))
|
||||||
|
|
||||||
(defn group-to-bool
|
(defn group-to-bool
|
||||||
[shape-id bool-type]
|
[shape-id bool-type]
|
||||||
|
|
51
frontend/src/app/main/data/workspace/collapse.cljs
Normal file
51
frontend/src/app/main/data/workspace/collapse.cljs
Normal file
|
@ -0,0 +1,51 @@
|
||||||
|
;; This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
;; License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
;; file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
;;
|
||||||
|
;; Copyright (c) UXBOX Labs SL
|
||||||
|
|
||||||
|
(ns app.main.data.workspace.collapse
|
||||||
|
(:require
|
||||||
|
[app.common.pages.helpers :as cph]
|
||||||
|
[app.common.uuid :as uuid]
|
||||||
|
[potok.core :as ptk]))
|
||||||
|
|
||||||
|
;; --- Shape attrs (Layers Sidebar)
|
||||||
|
|
||||||
|
(defn expand-all-parents
|
||||||
|
[ids objects]
|
||||||
|
(ptk/reify ::expand-all-parents
|
||||||
|
ptk/UpdateEvent
|
||||||
|
(update [_ state]
|
||||||
|
(let [expand-fn (fn [expanded]
|
||||||
|
(merge expanded
|
||||||
|
(->> ids
|
||||||
|
(map #(cph/get-parent-ids objects %))
|
||||||
|
flatten
|
||||||
|
(remove #(= % uuid/zero))
|
||||||
|
(map (fn [id] {id true}))
|
||||||
|
(into {}))))]
|
||||||
|
(update-in state [:workspace-local :expanded] expand-fn)))))
|
||||||
|
|
||||||
|
|
||||||
|
(defn toggle-collapse
|
||||||
|
[id]
|
||||||
|
(ptk/reify ::toggle-collapse
|
||||||
|
ptk/UpdateEvent
|
||||||
|
(update [_ state]
|
||||||
|
(update-in state [:workspace-local :expanded id] not))))
|
||||||
|
|
||||||
|
(defn expand-collapse
|
||||||
|
[id]
|
||||||
|
(ptk/reify ::expand-collapse
|
||||||
|
ptk/UpdateEvent
|
||||||
|
(update [_ state]
|
||||||
|
(assoc-in state [:workspace-local :expanded id] true))))
|
||||||
|
|
||||||
|
(defn collapse-all
|
||||||
|
[]
|
||||||
|
(ptk/reify ::collapse-all
|
||||||
|
ptk/UpdateEvent
|
||||||
|
(update [_ state]
|
||||||
|
(update state :workspace-local dissoc :expanded))))
|
||||||
|
|
|
@ -6,33 +6,16 @@
|
||||||
|
|
||||||
(ns app.main.data.workspace.common
|
(ns app.main.data.workspace.common
|
||||||
(:require
|
(:require
|
||||||
[app.common.data :as d]
|
|
||||||
[app.common.geom.proportions :as gpr]
|
|
||||||
[app.common.logging :as log]
|
[app.common.logging :as log]
|
||||||
[app.common.pages :as cp]
|
|
||||||
[app.common.pages.changes-builder :as pcb]
|
|
||||||
[app.common.pages.helpers :as cph]
|
|
||||||
[app.common.spec :as us]
|
|
||||||
[app.common.types.page :as ctp]
|
|
||||||
[app.common.types.shape :as cts]
|
|
||||||
[app.common.types.shape.interactions :as ctsi]
|
|
||||||
[app.common.uuid :as uuid]
|
|
||||||
[app.main.data.workspace.changes :as dch]
|
[app.main.data.workspace.changes :as dch]
|
||||||
[app.main.data.workspace.state-helpers :as wsh]
|
|
||||||
[app.main.data.workspace.undo :as dwu]
|
[app.main.data.workspace.undo :as dwu]
|
||||||
[app.main.streams :as ms]
|
|
||||||
[app.main.worker :as uw]
|
[app.main.worker :as uw]
|
||||||
[beicon.core :as rx]
|
[beicon.core :as rx]
|
||||||
[cljs.spec.alpha :as s]
|
|
||||||
[potok.core :as ptk]))
|
[potok.core :as ptk]))
|
||||||
|
|
||||||
;; Change this to :info :debug or :trace to debug this module
|
;; Change this to :info :debug or :trace to debug this module
|
||||||
(log/set-level! :warn)
|
(log/set-level! :warn)
|
||||||
|
|
||||||
(s/def ::shape-attrs ::cts/shape-attrs)
|
|
||||||
(s/def ::set-of-string (s/every string? :kind set?))
|
|
||||||
(s/def ::ordered-set-of-uuid (s/every uuid? :kind d/ordered-set?))
|
|
||||||
|
|
||||||
(defn initialized?
|
(defn initialized?
|
||||||
"Check if the state is properly intialized in a workspace. This means
|
"Check if the state is properly intialized in a workspace. This means
|
||||||
it has the `:current-page-id` and `:current-file-id` properly set."
|
it has the `:current-page-id` and `:current-file-id` properly set."
|
||||||
|
@ -56,57 +39,6 @@
|
||||||
(->> (uw/ask! msg)
|
(->> (uw/ask! msg)
|
||||||
(rx/map (constantly ::index-initialized)))))))
|
(rx/map (constantly ::index-initialized)))))))
|
||||||
|
|
||||||
;; --- Common Helpers & Events
|
|
||||||
|
|
||||||
(defn- extract-numeric-suffix
|
|
||||||
[basename]
|
|
||||||
(if-let [[_ p1 p2] (re-find #"(.*)-([0-9]+)$" basename)]
|
|
||||||
[p1 (+ 1 (d/parse-integer p2))]
|
|
||||||
[basename 1]))
|
|
||||||
|
|
||||||
(defn retrieve-used-names
|
|
||||||
[objects]
|
|
||||||
(into #{} (comp (map :name) (remove nil?)) (vals objects)))
|
|
||||||
|
|
||||||
|
|
||||||
(defn generate-unique-name
|
|
||||||
"A unique name generator"
|
|
||||||
[used basename]
|
|
||||||
(s/assert ::set-of-string used)
|
|
||||||
(s/assert ::us/string basename)
|
|
||||||
(if-not (contains? used basename)
|
|
||||||
basename
|
|
||||||
(let [[prefix initial] (extract-numeric-suffix basename)]
|
|
||||||
(loop [counter initial]
|
|
||||||
(let [candidate (str prefix "-" counter)]
|
|
||||||
(if (contains? used candidate)
|
|
||||||
(recur (inc counter))
|
|
||||||
candidate))))))
|
|
||||||
|
|
||||||
;; --- Shape attrs (Layers Sidebar)
|
|
||||||
|
|
||||||
(defn toggle-collapse
|
|
||||||
[id]
|
|
||||||
(ptk/reify ::toggle-collapse
|
|
||||||
ptk/UpdateEvent
|
|
||||||
(update [_ state]
|
|
||||||
(update-in state [:workspace-local :expanded id] not))))
|
|
||||||
|
|
||||||
(defn expand-collapse
|
|
||||||
[id]
|
|
||||||
(ptk/reify ::expand-collapse
|
|
||||||
ptk/UpdateEvent
|
|
||||||
(update [_ state]
|
|
||||||
(assoc-in state [:workspace-local :expanded id] true))))
|
|
||||||
|
|
||||||
(defn collapse-all
|
|
||||||
[]
|
|
||||||
(ptk/reify ::collapse-all
|
|
||||||
ptk/UpdateEvent
|
|
||||||
(update [_ state]
|
|
||||||
(update state :workspace-local dissoc :expanded))))
|
|
||||||
|
|
||||||
|
|
||||||
;; These functions should've been in `src/app/main/data/workspace/undo.cljs` but doing that causes
|
;; These functions should've been in `src/app/main/data/workspace/undo.cljs` but doing that causes
|
||||||
;; a circular dependency with `src/app/main/data/workspace/changes.cljs`
|
;; a circular dependency with `src/app/main/data/workspace/changes.cljs`
|
||||||
(def undo
|
(def undo
|
||||||
|
@ -177,315 +109,3 @@
|
||||||
:origin it
|
:origin it
|
||||||
:save-undo? false})))))))))))
|
:save-undo? false})))))))))))
|
||||||
|
|
||||||
|
|
||||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
||||||
;; Shapes
|
|
||||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
||||||
|
|
||||||
(defn expand-all-parents
|
|
||||||
[ids objects]
|
|
||||||
(ptk/reify ::expand-all-parents
|
|
||||||
ptk/UpdateEvent
|
|
||||||
(update [_ state]
|
|
||||||
(let [expand-fn (fn [expanded]
|
|
||||||
(merge expanded
|
|
||||||
(->> ids
|
|
||||||
(map #(cph/get-parent-ids objects %))
|
|
||||||
flatten
|
|
||||||
(remove #(= % uuid/zero))
|
|
||||||
(map (fn [id] {id true}))
|
|
||||||
(into {}))))]
|
|
||||||
(update-in state [:workspace-local :expanded] expand-fn)))))
|
|
||||||
|
|
||||||
;; --- Update Shape Attrs
|
|
||||||
|
|
||||||
;; NOTE: This is a generic implementation for update multiple shapes
|
|
||||||
;; in one single commit/undo entry.
|
|
||||||
|
|
||||||
|
|
||||||
(defn select-shapes
|
|
||||||
[ids]
|
|
||||||
(us/verify ::ordered-set-of-uuid ids)
|
|
||||||
(ptk/reify ::select-shapes
|
|
||||||
ptk/UpdateEvent
|
|
||||||
(update [_ state]
|
|
||||||
(assoc-in state [:workspace-local :selected] ids))
|
|
||||||
|
|
||||||
ptk/WatchEvent
|
|
||||||
(watch [_ state _]
|
|
||||||
(let [page-id (:current-page-id state)
|
|
||||||
objects (wsh/lookup-page-objects state page-id)]
|
|
||||||
(rx/of (expand-all-parents ids objects))))))
|
|
||||||
|
|
||||||
(declare clear-edition-mode)
|
|
||||||
|
|
||||||
(defn start-edition-mode
|
|
||||||
[id]
|
|
||||||
(us/assert ::us/uuid id)
|
|
||||||
(ptk/reify ::start-edition-mode
|
|
||||||
ptk/UpdateEvent
|
|
||||||
(update [_ state]
|
|
||||||
(let [objects (wsh/lookup-page-objects state)]
|
|
||||||
;; Can only edit objects that exist
|
|
||||||
(if (contains? objects id)
|
|
||||||
(-> state
|
|
||||||
(assoc-in [:workspace-local :selected] #{id})
|
|
||||||
(assoc-in [:workspace-local :edition] id))
|
|
||||||
state)))
|
|
||||||
|
|
||||||
ptk/WatchEvent
|
|
||||||
(watch [_ _ stream]
|
|
||||||
(->> stream
|
|
||||||
(rx/filter interrupt?)
|
|
||||||
(rx/take 1)
|
|
||||||
(rx/map (constantly clear-edition-mode))))))
|
|
||||||
|
|
||||||
;; If these event change modules review /src/app/main/data/workspace/path/undo.cljs
|
|
||||||
(def clear-edition-mode
|
|
||||||
(ptk/reify ::clear-edition-mode
|
|
||||||
ptk/UpdateEvent
|
|
||||||
(update [_ state]
|
|
||||||
(let [id (get-in state [:workspace-local :edition])]
|
|
||||||
(-> state
|
|
||||||
(update :workspace-local dissoc :edition)
|
|
||||||
(cond-> (some? id) (update-in [:workspace-local :edit-path] dissoc id)))))))
|
|
||||||
|
|
||||||
(defn get-shape-layer-position
|
|
||||||
[objects selected attrs]
|
|
||||||
|
|
||||||
;; Calculate the frame over which we're drawing
|
|
||||||
(let [position @ms/mouse-position
|
|
||||||
frame-id (:frame-id attrs (cph/frame-id-by-position objects position))
|
|
||||||
shape (when-not (empty? selected)
|
|
||||||
(cph/get-base-shape objects selected))]
|
|
||||||
|
|
||||||
;; When no shapes has been selected or we're over a different frame
|
|
||||||
;; we add it as the latest shape of that frame
|
|
||||||
(if (or (not shape) (not= (:frame-id shape) frame-id))
|
|
||||||
[frame-id frame-id nil]
|
|
||||||
|
|
||||||
;; Otherwise, we add it to next to the selected shape
|
|
||||||
(let [index (cph/get-position-on-parent objects (:id shape))
|
|
||||||
{:keys [frame-id parent-id]} shape]
|
|
||||||
[frame-id parent-id (inc index)]))))
|
|
||||||
|
|
||||||
(defn make-new-shape
|
|
||||||
[attrs objects selected]
|
|
||||||
(let [default-attrs (if (= :frame (:type attrs))
|
|
||||||
cp/default-frame-attrs
|
|
||||||
cp/default-shape-attrs)
|
|
||||||
|
|
||||||
selected-non-frames
|
|
||||||
(into #{} (comp (map (d/getf objects))
|
|
||||||
(remove cph/frame-shape?))
|
|
||||||
selected)
|
|
||||||
|
|
||||||
[frame-id parent-id index]
|
|
||||||
(get-shape-layer-position objects selected-non-frames attrs)]
|
|
||||||
|
|
||||||
(-> (merge default-attrs attrs)
|
|
||||||
(gpr/setup-proportions)
|
|
||||||
(assoc :frame-id frame-id
|
|
||||||
:parent-id parent-id
|
|
||||||
:index index))))
|
|
||||||
|
|
||||||
(defn add-shape
|
|
||||||
([attrs]
|
|
||||||
(add-shape attrs {}))
|
|
||||||
|
|
||||||
([attrs {:keys [no-select?]}]
|
|
||||||
(us/verify ::shape-attrs attrs)
|
|
||||||
(ptk/reify ::add-shape
|
|
||||||
ptk/WatchEvent
|
|
||||||
(watch [it state _]
|
|
||||||
(let [page-id (:current-page-id state)
|
|
||||||
objects (wsh/lookup-page-objects state page-id)
|
|
||||||
selected (wsh/lookup-selected state)
|
|
||||||
|
|
||||||
id (or (:id attrs) (uuid/next))
|
|
||||||
name (-> objects
|
|
||||||
(retrieve-used-names)
|
|
||||||
(generate-unique-name (:name attrs)))
|
|
||||||
|
|
||||||
shape (make-new-shape
|
|
||||||
(assoc attrs :id id :name name)
|
|
||||||
objects
|
|
||||||
selected)
|
|
||||||
|
|
||||||
changes (-> (pcb/empty-changes it page-id)
|
|
||||||
(pcb/with-objects objects)
|
|
||||||
(pcb/add-object shape)
|
|
||||||
(cond-> (some? (:parent-id attrs))
|
|
||||||
(pcb/change-parent (:parent-id attrs) [shape])))]
|
|
||||||
|
|
||||||
(rx/concat
|
|
||||||
(rx/of (dch/commit-changes changes)
|
|
||||||
(when-not no-select?
|
|
||||||
(select-shapes (d/ordered-set id))))
|
|
||||||
(when (= :text (:type attrs))
|
|
||||||
(->> (rx/of (start-edition-mode id))
|
|
||||||
(rx/observe-on :async)))))))))
|
|
||||||
|
|
||||||
(defn move-shapes-into-frame [frame-id shapes]
|
|
||||||
(ptk/reify ::move-shapes-into-frame
|
|
||||||
ptk/WatchEvent
|
|
||||||
(watch [it state _]
|
|
||||||
(let [page-id (:current-page-id state)
|
|
||||||
objects (wsh/lookup-page-objects state page-id)
|
|
||||||
|
|
||||||
to-move-shapes
|
|
||||||
(into []
|
|
||||||
(map (d/getf objects))
|
|
||||||
(reverse (cph/sort-z-index objects shapes)))
|
|
||||||
|
|
||||||
changes
|
|
||||||
(when (d/not-empty? to-move-shapes)
|
|
||||||
(-> (pcb/empty-changes it page-id)
|
|
||||||
(pcb/with-objects objects)
|
|
||||||
(pcb/change-parent frame-id to-move-shapes 0)))]
|
|
||||||
|
|
||||||
(if (some? changes)
|
|
||||||
(rx/of (dch/commit-changes changes))
|
|
||||||
(rx/empty))))))
|
|
||||||
|
|
||||||
(s/def ::set-of-uuid
|
|
||||||
(s/every ::us/uuid :kind set?))
|
|
||||||
|
|
||||||
(defn delete-shapes
|
|
||||||
[ids]
|
|
||||||
(us/assert ::set-of-uuid ids)
|
|
||||||
(ptk/reify ::delete-shapes
|
|
||||||
ptk/WatchEvent
|
|
||||||
(watch [it state _]
|
|
||||||
(let [page-id (:current-page-id state)
|
|
||||||
objects (wsh/lookup-page-objects state page-id)
|
|
||||||
page (wsh/lookup-page state page-id)
|
|
||||||
|
|
||||||
ids (cph/clean-loops objects ids)
|
|
||||||
|
|
||||||
groups-to-unmask
|
|
||||||
(reduce (fn [group-ids id]
|
|
||||||
;; When the shape to delete is the mask of a masked group,
|
|
||||||
;; the mask condition must be removed, and it must be
|
|
||||||
;; converted to a normal group.
|
|
||||||
(let [obj (get objects id)
|
|
||||||
parent (get objects (:parent-id obj))]
|
|
||||||
(if (and (:masked-group? parent)
|
|
||||||
(= id (first (:shapes parent))))
|
|
||||||
(conj group-ids (:id parent))
|
|
||||||
group-ids)))
|
|
||||||
#{}
|
|
||||||
ids)
|
|
||||||
|
|
||||||
interacting-shapes
|
|
||||||
(filter (fn [shape]
|
|
||||||
;; If any of the deleted shapes is the destination of
|
|
||||||
;; some interaction, this must be deleted, too.
|
|
||||||
(let [interactions (:interactions shape)]
|
|
||||||
(some #(and (ctsi/has-destination %)
|
|
||||||
(contains? ids (:destination %)))
|
|
||||||
interactions)))
|
|
||||||
(vals objects))
|
|
||||||
|
|
||||||
;; If any of the deleted shapes is a frame with guides
|
|
||||||
guides (into {} (map (juxt :id identity) (->> (get-in page [:options :guides])
|
|
||||||
(vals)
|
|
||||||
(filter #(not (contains? ids (:frame-id %)))))))
|
|
||||||
|
|
||||||
starting-flows
|
|
||||||
(filter (fn [flow]
|
|
||||||
;; If any of the deleted is a frame that starts a flow,
|
|
||||||
;; this must be deleted, too.
|
|
||||||
(contains? ids (:starting-frame flow)))
|
|
||||||
(-> page :options :flows))
|
|
||||||
|
|
||||||
all-parents
|
|
||||||
(reduce (fn [res id]
|
|
||||||
;; All parents of any deleted shape must be resized.
|
|
||||||
(into res (cph/get-parent-ids objects id)))
|
|
||||||
(d/ordered-set)
|
|
||||||
ids)
|
|
||||||
|
|
||||||
all-children
|
|
||||||
(->> ids ;; Children of deleted shapes must be also deleted.
|
|
||||||
(reduce (fn [res id]
|
|
||||||
(into res (cph/get-children-ids objects id)))
|
|
||||||
[])
|
|
||||||
(reverse)
|
|
||||||
(into (d/ordered-set)))
|
|
||||||
|
|
||||||
find-all-empty-parents (fn recursive-find-empty-parents [empty-parents]
|
|
||||||
(let [all-ids (into empty-parents ids)
|
|
||||||
empty-parents-xform
|
|
||||||
(comp
|
|
||||||
(map (fn [id] (get objects id)))
|
|
||||||
(map (fn [{:keys [shapes type] :as obj}]
|
|
||||||
(when (and (= :group type)
|
|
||||||
(zero? (count (remove #(contains? all-ids %) shapes))))
|
|
||||||
obj)))
|
|
||||||
(take-while some?)
|
|
||||||
(map :id))
|
|
||||||
calculated-empty-parents (into #{} empty-parents-xform all-parents)]
|
|
||||||
|
|
||||||
(if (= empty-parents calculated-empty-parents)
|
|
||||||
empty-parents
|
|
||||||
(recursive-find-empty-parents calculated-empty-parents))))
|
|
||||||
|
|
||||||
empty-parents
|
|
||||||
;; Any parent whose children are all deleted, must be deleted too.
|
|
||||||
(into (d/ordered-set) (find-all-empty-parents #{}))
|
|
||||||
|
|
||||||
changes (-> (pcb/empty-changes it page-id)
|
|
||||||
(pcb/with-page page)
|
|
||||||
(pcb/with-objects objects)
|
|
||||||
(pcb/set-page-option :guides guides)
|
|
||||||
(pcb/remove-objects all-children)
|
|
||||||
(pcb/remove-objects ids)
|
|
||||||
(pcb/remove-objects empty-parents)
|
|
||||||
(pcb/resize-parents all-parents)
|
|
||||||
(pcb/update-shapes groups-to-unmask
|
|
||||||
(fn [shape]
|
|
||||||
(assoc shape :masked-group? false)))
|
|
||||||
(pcb/update-shapes (map :id interacting-shapes)
|
|
||||||
(fn [shape]
|
|
||||||
(update shape :interactions
|
|
||||||
(fn [interactions]
|
|
||||||
(when interactions
|
|
||||||
(d/removev #(and (ctsi/has-destination %)
|
|
||||||
(contains? ids (:destination %)))
|
|
||||||
interactions))))))
|
|
||||||
(cond->
|
|
||||||
(seq starting-flows)
|
|
||||||
(pcb/update-page-option :flows (fn [flows]
|
|
||||||
(reduce #(ctp/remove-flow %1 (:id %2))
|
|
||||||
flows
|
|
||||||
starting-flows)))))]
|
|
||||||
|
|
||||||
(rx/of (dch/commit-changes changes))))))
|
|
||||||
|
|
||||||
;; --- Add shape to Workspace
|
|
||||||
|
|
||||||
(defn- viewport-center
|
|
||||||
[state]
|
|
||||||
(let [{:keys [x y width height]} (get-in state [:workspace-local :vbox])]
|
|
||||||
[(+ x (/ width 2)) (+ y (/ height 2))]))
|
|
||||||
|
|
||||||
(defn create-and-add-shape
|
|
||||||
[type frame-x frame-y data]
|
|
||||||
(ptk/reify ::create-and-add-shape
|
|
||||||
ptk/WatchEvent
|
|
||||||
(watch [_ state _]
|
|
||||||
(let [{:keys [width height]} data
|
|
||||||
|
|
||||||
[vbc-x vbc-y] (viewport-center state)
|
|
||||||
x (:x data (- vbc-x (/ width 2)))
|
|
||||||
y (:y data (- vbc-y (/ height 2)))
|
|
||||||
page-id (:current-page-id state)
|
|
||||||
frame-id (-> (wsh/lookup-page-objects state page-id)
|
|
||||||
(cph/frame-id-by-position {:x frame-x :y frame-y}))
|
|
||||||
shape (-> (cp/make-minimal-shape type)
|
|
||||||
(merge data)
|
|
||||||
(merge {:x x :y y})
|
|
||||||
(assoc :frame-id frame-id)
|
|
||||||
(cp/setup-rect-selrect))]
|
|
||||||
(rx/of (add-shape shape))))))
|
|
||||||
|
|
|
@ -11,7 +11,7 @@
|
||||||
[app.common.math :as mth]
|
[app.common.math :as mth]
|
||||||
[app.common.pages :as cp]
|
[app.common.pages :as cp]
|
||||||
[app.common.pages.helpers :as cph]
|
[app.common.pages.helpers :as cph]
|
||||||
[app.main.data.workspace.common :as dwc]
|
[app.main.data.workspace.shapes :as dwsh]
|
||||||
[app.main.data.workspace.state-helpers :as wsh]
|
[app.main.data.workspace.state-helpers :as wsh]
|
||||||
[app.main.data.workspace.undo :as dwu]
|
[app.main.data.workspace.undo :as dwu]
|
||||||
[app.main.worker :as uw]
|
[app.main.worker :as uw]
|
||||||
|
@ -66,7 +66,7 @@
|
||||||
(rx/of (dwu/start-undo-transaction))
|
(rx/of (dwu/start-undo-transaction))
|
||||||
(rx/empty))
|
(rx/empty))
|
||||||
|
|
||||||
(rx/of (dwc/add-shape shape {:no-select? (= tool :curve)}))
|
(rx/of (dwsh/add-shape shape {:no-select? (= tool :curve)}))
|
||||||
|
|
||||||
(if (= :frame (:type shape))
|
(if (= :frame (:type shape))
|
||||||
(->> (uw/ask! {:cmd :selection/query
|
(->> (uw/ask! {:cmd :selection/query
|
||||||
|
@ -75,7 +75,7 @@
|
||||||
:include-frames? true
|
:include-frames? true
|
||||||
:full-frame? true})
|
:full-frame? true})
|
||||||
(rx/map #(cph/clean-loops objects %))
|
(rx/map #(cph/clean-loops objects %))
|
||||||
(rx/map #(dwc/move-shapes-into-frame (:id shape) %)))
|
(rx/map #(dwsh/move-shapes-into-frame (:id shape) %)))
|
||||||
(rx/empty)))))
|
(rx/empty)))))
|
||||||
|
|
||||||
;; Delay so the mouse event can read the drawing state
|
;; Delay so the mouse event can read the drawing state
|
||||||
|
|
47
frontend/src/app/main/data/workspace/edition.cljs
Normal file
47
frontend/src/app/main/data/workspace/edition.cljs
Normal file
|
@ -0,0 +1,47 @@
|
||||||
|
;; This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
;; License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
;; file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
;;
|
||||||
|
;; Copyright (c) UXBOX Labs SL
|
||||||
|
|
||||||
|
(ns app.main.data.workspace.edition
|
||||||
|
(:require
|
||||||
|
[app.common.spec :as us]
|
||||||
|
[app.main.data.workspace.state-helpers :as wsh]
|
||||||
|
[beicon.core :as rx]
|
||||||
|
[potok.core :as ptk]))
|
||||||
|
|
||||||
|
(defn interrupt? [e] (= e :interrupt))
|
||||||
|
|
||||||
|
(declare clear-edition-mode)
|
||||||
|
|
||||||
|
(defn start-edition-mode
|
||||||
|
[id]
|
||||||
|
(us/assert ::us/uuid id)
|
||||||
|
(ptk/reify ::start-edition-mode
|
||||||
|
ptk/UpdateEvent
|
||||||
|
(update [_ state]
|
||||||
|
(let [objects (wsh/lookup-page-objects state)]
|
||||||
|
;; Can only edit objects that exist
|
||||||
|
(if (contains? objects id)
|
||||||
|
(-> state
|
||||||
|
(assoc-in [:workspace-local :selected] #{id})
|
||||||
|
(assoc-in [:workspace-local :edition] id))
|
||||||
|
state)))
|
||||||
|
|
||||||
|
ptk/WatchEvent
|
||||||
|
(watch [_ _ stream]
|
||||||
|
(->> stream
|
||||||
|
(rx/filter interrupt?)
|
||||||
|
(rx/take 1)
|
||||||
|
(rx/map (constantly clear-edition-mode))))))
|
||||||
|
|
||||||
|
;; If these event change modules review /src/app/main/data/workspace/path/undo.cljs
|
||||||
|
(def clear-edition-mode
|
||||||
|
(ptk/reify ::clear-edition-mode
|
||||||
|
ptk/UpdateEvent
|
||||||
|
(update [_ state]
|
||||||
|
(let [id (get-in state [:workspace-local :edition])]
|
||||||
|
(-> state
|
||||||
|
(update :workspace-local dissoc :edition)
|
||||||
|
(cond-> (some? id) (update-in [:workspace-local :edit-path] dissoc id)))))))
|
|
@ -12,8 +12,9 @@
|
||||||
[app.common.pages.changes-builder :as pcb]
|
[app.common.pages.changes-builder :as pcb]
|
||||||
[app.common.pages.helpers :as cph]
|
[app.common.pages.helpers :as cph]
|
||||||
[app.main.data.workspace.changes :as dch]
|
[app.main.data.workspace.changes :as dch]
|
||||||
[app.main.data.workspace.common :as dwc]
|
[app.main.data.workspace.selection :as dws]
|
||||||
[app.main.data.workspace.state-helpers :as wsh]
|
[app.main.data.workspace.state-helpers :as wsh]
|
||||||
|
[app.util.names :as un]
|
||||||
[beicon.core :as rx]
|
[beicon.core :as rx]
|
||||||
[potok.core :as ptk]))
|
[potok.core :as ptk]))
|
||||||
|
|
||||||
|
@ -70,8 +71,8 @@
|
||||||
(= (count shapes) 1)
|
(= (count shapes) 1)
|
||||||
(= (:type (first shapes)) :group))
|
(= (:type (first shapes)) :group))
|
||||||
(:name (first shapes))
|
(:name (first shapes))
|
||||||
(-> (dwc/retrieve-used-names objects)
|
(-> (un/retrieve-used-names objects)
|
||||||
(dwc/generate-unique-name base-name)))
|
(un/generate-unique-name base-name)))
|
||||||
|
|
||||||
selrect (gsh/selection-rect shapes)
|
selrect (gsh/selection-rect shapes)
|
||||||
group (-> (cp/make-minimal-group frame-id selrect gname)
|
group (-> (cp/make-minimal-group frame-id selrect gname)
|
||||||
|
@ -142,7 +143,7 @@
|
||||||
(let [[group changes]
|
(let [[group changes]
|
||||||
(prepare-create-group it objects page-id shapes "Group-1" false)]
|
(prepare-create-group it objects page-id shapes "Group-1" false)]
|
||||||
(rx/of (dch/commit-changes changes)
|
(rx/of (dch/commit-changes changes)
|
||||||
(dwc/select-shapes (d/ordered-set (:id group))))))))))
|
(dws/select-shapes (d/ordered-set (:id group))))))))))
|
||||||
|
|
||||||
(def ungroup-selected
|
(def ungroup-selected
|
||||||
(ptk/reify ::ungroup-selected
|
(ptk/reify ::ungroup-selected
|
||||||
|
@ -203,7 +204,7 @@
|
||||||
(pcb/resize-parents [(:id group)]))]
|
(pcb/resize-parents [(:id group)]))]
|
||||||
|
|
||||||
(rx/of (dch/commit-changes changes)
|
(rx/of (dch/commit-changes changes)
|
||||||
(dwc/select-shapes (d/ordered-set (:id group))))))))))
|
(dws/select-shapes (d/ordered-set (:id group))))))))))
|
||||||
|
|
||||||
(def unmask-group
|
(def unmask-group
|
||||||
(ptk/reify ::unmask-group
|
(ptk/reify ::unmask-group
|
||||||
|
|
|
@ -15,9 +15,9 @@
|
||||||
[app.common.types.shape.interactions :as ctsi]
|
[app.common.types.shape.interactions :as ctsi]
|
||||||
[app.common.uuid :as uuid]
|
[app.common.uuid :as uuid]
|
||||||
[app.main.data.workspace.changes :as dch]
|
[app.main.data.workspace.changes :as dch]
|
||||||
[app.main.data.workspace.common :as dwc]
|
|
||||||
[app.main.data.workspace.state-helpers :as wsh]
|
[app.main.data.workspace.state-helpers :as wsh]
|
||||||
[app.main.streams :as ms]
|
[app.main.streams :as ms]
|
||||||
|
[app.util.names :as un]
|
||||||
[beicon.core :as rx]
|
[beicon.core :as rx]
|
||||||
[potok.core :as ptk]))
|
[potok.core :as ptk]))
|
||||||
|
|
||||||
|
@ -32,7 +32,7 @@
|
||||||
|
|
||||||
flows (get-in page [:options :flows] [])
|
flows (get-in page [:options :flows] [])
|
||||||
unames (into #{} (map :name flows))
|
unames (into #{} (map :name flows))
|
||||||
name (dwc/generate-unique-name unames "Flow-1")
|
name (un/generate-unique-name unames "Flow-1")
|
||||||
|
|
||||||
new-flow {:id (uuid/next)
|
new-flow {:id (uuid/next)
|
||||||
:name name
|
:name name
|
||||||
|
|
|
@ -22,14 +22,15 @@
|
||||||
[app.main.data.events :as ev]
|
[app.main.data.events :as ev]
|
||||||
[app.main.data.messages :as dm]
|
[app.main.data.messages :as dm]
|
||||||
[app.main.data.workspace.changes :as dch]
|
[app.main.data.workspace.changes :as dch]
|
||||||
[app.main.data.workspace.common :as dwc]
|
|
||||||
[app.main.data.workspace.groups :as dwg]
|
[app.main.data.workspace.groups :as dwg]
|
||||||
[app.main.data.workspace.libraries-helpers :as dwlh]
|
[app.main.data.workspace.libraries-helpers :as dwlh]
|
||||||
|
[app.main.data.workspace.selection :as dws]
|
||||||
[app.main.data.workspace.state-helpers :as wsh]
|
[app.main.data.workspace.state-helpers :as wsh]
|
||||||
[app.main.data.workspace.undo :as dwu]
|
[app.main.data.workspace.undo :as dwu]
|
||||||
[app.main.repo :as rp]
|
[app.main.repo :as rp]
|
||||||
[app.main.store :as st]
|
[app.main.store :as st]
|
||||||
[app.util.i18n :refer [tr]]
|
[app.util.i18n :refer [tr]]
|
||||||
|
[app.util.names :as un]
|
||||||
[app.util.router :as rt]
|
[app.util.router :as rt]
|
||||||
[app.util.time :as dt]
|
[app.util.time :as dt]
|
||||||
[beicon.core :as rx]
|
[beicon.core :as rx]
|
||||||
|
@ -295,7 +296,7 @@
|
||||||
(dwlh/generate-add-component it shapes objects page-id file-id)]
|
(dwlh/generate-add-component it shapes objects page-id file-id)]
|
||||||
(when-not (empty? (:redo-changes changes))
|
(when-not (empty? (:redo-changes changes))
|
||||||
(rx/of (dch/commit-changes changes)
|
(rx/of (dch/commit-changes changes)
|
||||||
(dwc/select-shapes (d/ordered-set (:id group)))))))))))
|
(dws/select-shapes (d/ordered-set (:id group)))))))))))
|
||||||
|
|
||||||
(defn add-component
|
(defn add-component
|
||||||
"Add a new component to current file library, from the currently selected shapes.
|
"Add a new component to current file library, from the currently selected shapes.
|
||||||
|
@ -351,7 +352,7 @@
|
||||||
component (cph/get-component libraries id)
|
component (cph/get-component libraries id)
|
||||||
all-components (-> state :workspace-data :components vals)
|
all-components (-> state :workspace-data :components vals)
|
||||||
unames (into #{} (map :name) all-components)
|
unames (into #{} (map :name) all-components)
|
||||||
new-name (dwc/generate-unique-name unames (:name component))
|
new-name (un/generate-unique-name unames (:name component))
|
||||||
|
|
||||||
[new-shape new-shapes _updated-shapes]
|
[new-shape new-shapes _updated-shapes]
|
||||||
(dwlh/duplicate-component component)
|
(dwlh/duplicate-component component)
|
||||||
|
@ -401,7 +402,7 @@
|
||||||
page
|
page
|
||||||
libraries)]
|
libraries)]
|
||||||
(rx/of (dch/commit-changes changes)
|
(rx/of (dch/commit-changes changes)
|
||||||
(dwc/select-shapes (d/ordered-set (:id new-shape))))))))
|
(dws/select-shapes (d/ordered-set (:id new-shape))))))))
|
||||||
|
|
||||||
(defn detach-component
|
(defn detach-component
|
||||||
"Remove all references to components in the shape with the given id,
|
"Remove all references to components in the shape with the given id,
|
||||||
|
|
|
@ -16,9 +16,9 @@
|
||||||
[app.common.spec :as us]
|
[app.common.spec :as us]
|
||||||
[app.common.text :as txt]
|
[app.common.text :as txt]
|
||||||
[app.common.types.color :as ctc]
|
[app.common.types.color :as ctc]
|
||||||
[app.main.data.workspace.common :as dwc]
|
|
||||||
[app.main.data.workspace.groups :as dwg]
|
[app.main.data.workspace.groups :as dwg]
|
||||||
[app.main.data.workspace.state-helpers :as wsh]
|
[app.main.data.workspace.state-helpers :as wsh]
|
||||||
|
[app.util.names :as un]
|
||||||
[cljs.spec.alpha :as s]
|
[cljs.spec.alpha :as s]
|
||||||
[clojure.set :as set]))
|
[clojure.set :as set]))
|
||||||
|
|
||||||
|
@ -144,13 +144,13 @@
|
||||||
delta (gpt/subtract position orig-pos)
|
delta (gpt/subtract position orig-pos)
|
||||||
|
|
||||||
objects (:objects page)
|
objects (:objects page)
|
||||||
unames (volatile! (dwc/retrieve-used-names objects))
|
unames (volatile! (un/retrieve-used-names objects))
|
||||||
|
|
||||||
frame-id (cph/frame-id-by-position objects (gpt/add orig-pos delta))
|
frame-id (cph/frame-id-by-position objects (gpt/add orig-pos delta))
|
||||||
|
|
||||||
update-new-shape
|
update-new-shape
|
||||||
(fn [new-shape original-shape]
|
(fn [new-shape original-shape]
|
||||||
(let [new-name (dwc/generate-unique-name @unames (:name new-shape))]
|
(let [new-name (un/generate-unique-name @unames (:name new-shape))]
|
||||||
|
|
||||||
(when (nil? (:parent-id original-shape))
|
(when (nil? (:parent-id original-shape))
|
||||||
(vswap! unames conj new-name))
|
(vswap! unames conj new-name))
|
||||||
|
|
|
@ -10,8 +10,8 @@
|
||||||
[app.common.spec :as us]
|
[app.common.spec :as us]
|
||||||
[app.main.data.media :as dmm]
|
[app.main.data.media :as dmm]
|
||||||
[app.main.data.messages :as dm]
|
[app.main.data.messages :as dm]
|
||||||
[app.main.data.workspace.common :as dwc]
|
|
||||||
[app.main.data.workspace.libraries :as dwl]
|
[app.main.data.workspace.libraries :as dwl]
|
||||||
|
[app.main.data.workspace.shapes :as dwsh]
|
||||||
[app.main.data.workspace.svg-upload :as svg]
|
[app.main.data.workspace.svg-upload :as svg]
|
||||||
[app.main.repo :as rp]
|
[app.main.repo :as rp]
|
||||||
[app.main.store :as st]
|
[app.main.store :as st]
|
||||||
|
@ -72,7 +72,7 @@
|
||||||
:height height
|
:height height
|
||||||
:mtype mtype
|
:mtype mtype
|
||||||
:id id}}]
|
:id id}}]
|
||||||
(rx/of (dwc/create-and-add-shape :image x y shape))))))
|
(rx/of (dwsh/create-and-add-shape :image x y shape))))))
|
||||||
|
|
||||||
(defn svg-uploaded
|
(defn svg-uploaded
|
||||||
[svg-data file-id position]
|
[svg-data file-id position]
|
||||||
|
|
|
@ -13,8 +13,8 @@
|
||||||
[app.common.path.shapes-to-path :as upsp]
|
[app.common.path.shapes-to-path :as upsp]
|
||||||
[app.common.spec :as us]
|
[app.common.spec :as us]
|
||||||
[app.main.data.workspace.changes :as dch]
|
[app.main.data.workspace.changes :as dch]
|
||||||
[app.main.data.workspace.common :as dwc]
|
|
||||||
[app.main.data.workspace.drawing.common :as dwdc]
|
[app.main.data.workspace.drawing.common :as dwdc]
|
||||||
|
[app.main.data.workspace.edition :as dwe]
|
||||||
[app.main.data.workspace.path.changes :as changes]
|
[app.main.data.workspace.path.changes :as changes]
|
||||||
[app.main.data.workspace.path.common :as common]
|
[app.main.data.workspace.path.common :as common]
|
||||||
[app.main.data.workspace.path.helpers :as helpers]
|
[app.main.data.workspace.path.helpers :as helpers]
|
||||||
|
@ -276,7 +276,7 @@
|
||||||
(watch [_ _ _]
|
(watch [_ _ _]
|
||||||
(rx/of (setup-frame-path)
|
(rx/of (setup-frame-path)
|
||||||
(dwdc/handle-finish-drawing)
|
(dwdc/handle-finish-drawing)
|
||||||
(dwc/start-edition-mode shape-id)
|
(dwe/start-edition-mode shape-id)
|
||||||
(change-edit-mode :draw)))))
|
(change-edit-mode :draw)))))
|
||||||
|
|
||||||
(defn handle-new-shape
|
(defn handle-new-shape
|
||||||
|
|
|
@ -13,7 +13,7 @@
|
||||||
[app.common.path.shapes-to-path :as upsp]
|
[app.common.path.shapes-to-path :as upsp]
|
||||||
[app.common.path.subpaths :as ups]
|
[app.common.path.subpaths :as ups]
|
||||||
[app.main.data.workspace.changes :as dch]
|
[app.main.data.workspace.changes :as dch]
|
||||||
[app.main.data.workspace.common :as dwc]
|
[app.main.data.workspace.edition :as dwe]
|
||||||
[app.main.data.workspace.path.changes :as changes]
|
[app.main.data.workspace.path.changes :as changes]
|
||||||
[app.main.data.workspace.path.drawing :as drawing]
|
[app.main.data.workspace.path.drawing :as drawing]
|
||||||
[app.main.data.workspace.path.helpers :as helpers]
|
[app.main.data.workspace.path.helpers :as helpers]
|
||||||
|
@ -64,7 +64,7 @@
|
||||||
(let [changes (changes/generate-path-changes it objects page-id shape (:content shape) new-content)]
|
(let [changes (changes/generate-path-changes it objects page-id shape (:content shape) new-content)]
|
||||||
(if (empty? new-content)
|
(if (empty? new-content)
|
||||||
(rx/of (dch/commit-changes changes)
|
(rx/of (dch/commit-changes changes)
|
||||||
dwc/clear-edition-mode)
|
dwe/clear-edition-mode)
|
||||||
(rx/of (dch/commit-changes changes)
|
(rx/of (dch/commit-changes changes)
|
||||||
(selection/update-selection point-change)
|
(selection/update-selection point-change)
|
||||||
(fn [state] (update-in state [:workspace-local :edit-path id] dissoc :content-modifiers :moving-nodes :moving-handler))))))))))
|
(fn [state] (update-in state [:workspace-local :edit-path id] dissoc :content-modifiers :moving-nodes :moving-handler))))))))))
|
||||||
|
|
|
@ -9,7 +9,7 @@
|
||||||
[app.common.path.shapes-to-path :as upsp]
|
[app.common.path.shapes-to-path :as upsp]
|
||||||
[app.common.path.subpaths :as ups]
|
[app.common.path.subpaths :as ups]
|
||||||
[app.main.data.workspace.changes :as dch]
|
[app.main.data.workspace.changes :as dch]
|
||||||
[app.main.data.workspace.common :as dwc]
|
[app.main.data.workspace.edition :as dwe]
|
||||||
[app.main.data.workspace.path.changes :as changes]
|
[app.main.data.workspace.path.changes :as changes]
|
||||||
[app.main.data.workspace.path.state :as st]
|
[app.main.data.workspace.path.state :as st]
|
||||||
[app.main.data.workspace.state-helpers :as wsh]
|
[app.main.data.workspace.state-helpers :as wsh]
|
||||||
|
@ -40,7 +40,7 @@
|
||||||
(rx/of (dch/update-shapes [id] upsp/convert-to-path))
|
(rx/of (dch/update-shapes [id] upsp/convert-to-path))
|
||||||
(rx/of (dch/commit-changes changes)
|
(rx/of (dch/commit-changes changes)
|
||||||
(when (empty? new-content)
|
(when (empty? new-content)
|
||||||
dwc/clear-edition-mode))))))))))
|
dwe/clear-edition-mode))))))))))
|
||||||
|
|
||||||
(defn make-corner
|
(defn make-corner
|
||||||
([]
|
([]
|
||||||
|
|
|
@ -19,13 +19,14 @@
|
||||||
[app.common.uuid :as uuid]
|
[app.common.uuid :as uuid]
|
||||||
[app.main.data.modal :as md]
|
[app.main.data.modal :as md]
|
||||||
[app.main.data.workspace.changes :as dch]
|
[app.main.data.workspace.changes :as dch]
|
||||||
[app.main.data.workspace.common :as dwc]
|
[app.main.data.workspace.collapse :as dwc]
|
||||||
[app.main.data.workspace.state-helpers :as wsh]
|
[app.main.data.workspace.state-helpers :as wsh]
|
||||||
[app.main.data.workspace.thumbnails :as dwt]
|
[app.main.data.workspace.thumbnails :as dwt]
|
||||||
[app.main.data.workspace.zoom :as dwz]
|
[app.main.data.workspace.zoom :as dwz]
|
||||||
[app.main.refs :as refs]
|
[app.main.refs :as refs]
|
||||||
[app.main.streams :as ms]
|
[app.main.streams :as ms]
|
||||||
[app.main.worker :as uw]
|
[app.main.worker :as uw]
|
||||||
|
[app.util.names :as un]
|
||||||
[beicon.core :as rx]
|
[beicon.core :as rx]
|
||||||
[cljs.spec.alpha :as s]
|
[cljs.spec.alpha :as s]
|
||||||
[clojure.set :as set]
|
[clojure.set :as set]
|
||||||
|
@ -41,6 +42,8 @@
|
||||||
(s/def ::set-of-string
|
(s/def ::set-of-string
|
||||||
(s/every string? :kind set?))
|
(s/every string? :kind set?))
|
||||||
|
|
||||||
|
(defn interrupt? [e] (= e :interrupt))
|
||||||
|
|
||||||
;; --- Selection Rect
|
;; --- Selection Rect
|
||||||
|
|
||||||
(declare select-shapes-by-current-selrect)
|
(declare select-shapes-by-current-selrect)
|
||||||
|
@ -59,7 +62,7 @@
|
||||||
ptk/WatchEvent
|
ptk/WatchEvent
|
||||||
(watch [_ state stream]
|
(watch [_ state stream]
|
||||||
(let [zoom (get-in state [:workspace-local :zoom] 1)
|
(let [zoom (get-in state [:workspace-local :zoom] 1)
|
||||||
stop? (fn [event] (or (dwc/interrupt? event) (ms/mouse-up? event)))
|
stop? (fn [event] (or (interrupt? event) (ms/mouse-up? event)))
|
||||||
stoper (->> stream (rx/filter stop?))
|
stoper (->> stream (rx/filter stop?))
|
||||||
|
|
||||||
init-selrect
|
init-selrect
|
||||||
|
@ -281,7 +284,7 @@
|
||||||
move to the desired position, and recalculate parents and frames as needed."
|
move to the desired position, and recalculate parents and frames as needed."
|
||||||
[all-objects page ids delta it]
|
[all-objects page ids delta it]
|
||||||
(let [shapes (map (d/getf all-objects) ids)
|
(let [shapes (map (d/getf all-objects) ids)
|
||||||
unames (volatile! (dwc/retrieve-used-names (:objects page)))
|
unames (volatile! (un/retrieve-used-names (:objects page)))
|
||||||
update-unames! (fn [new-name] (vswap! unames conj new-name))
|
update-unames! (fn [new-name] (vswap! unames conj new-name))
|
||||||
all-ids (reduce #(into %1 (cons %2 (cph/get-children-ids all-objects %2))) (d/ordered-set) ids)
|
all-ids (reduce #(into %1 (cons %2 (cph/get-children-ids all-objects %2))) (d/ordered-set) ids)
|
||||||
ids-map (into {} (map #(vector % (uuid/next))) all-ids)
|
ids-map (into {} (map #(vector % (uuid/next))) all-ids)
|
||||||
|
@ -316,7 +319,7 @@
|
||||||
(defn- prepare-duplicate-frame-change
|
(defn- prepare-duplicate-frame-change
|
||||||
[changes objects page unames update-unames! ids-map obj delta]
|
[changes objects page unames update-unames! ids-map obj delta]
|
||||||
(let [new-id (ids-map (:id obj))
|
(let [new-id (ids-map (:id obj))
|
||||||
frame-name (dwc/generate-unique-name @unames (:name obj))
|
frame-name (un/generate-unique-name @unames (:name obj))
|
||||||
_ (update-unames! frame-name)
|
_ (update-unames! frame-name)
|
||||||
|
|
||||||
new-frame (-> obj
|
new-frame (-> obj
|
||||||
|
@ -351,7 +354,7 @@
|
||||||
(if (some? obj)
|
(if (some? obj)
|
||||||
(let [new-id (ids-map (:id obj))
|
(let [new-id (ids-map (:id obj))
|
||||||
parent-id (or parent-id frame-id)
|
parent-id (or parent-id frame-id)
|
||||||
name (dwc/generate-unique-name @unames (:name obj))
|
name (un/generate-unique-name @unames (:name obj))
|
||||||
_ (update-unames! name)
|
_ (update-unames! name)
|
||||||
|
|
||||||
new-obj (-> obj
|
new-obj (-> obj
|
||||||
|
@ -392,7 +395,7 @@
|
||||||
(let [update-flows (fn [flows]
|
(let [update-flows (fn [flows]
|
||||||
(reduce
|
(reduce
|
||||||
(fn [flows frame]
|
(fn [flows frame]
|
||||||
(let [name (dwc/generate-unique-name @unames "Flow-1")
|
(let [name (un/generate-unique-name @unames "Flow-1")
|
||||||
_ (vswap! unames conj name)
|
_ (vswap! unames conj name)
|
||||||
new-flow {:id (uuid/next)
|
new-flow {:id (uuid/next)
|
||||||
:name name
|
:name name
|
||||||
|
|
268
frontend/src/app/main/data/workspace/shapes.cljs
Normal file
268
frontend/src/app/main/data/workspace/shapes.cljs
Normal file
|
@ -0,0 +1,268 @@
|
||||||
|
;; This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
;; License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
;; file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
;;
|
||||||
|
;; Copyright (c) UXBOX Labs SL
|
||||||
|
|
||||||
|
(ns app.main.data.workspace.shapes
|
||||||
|
(:require
|
||||||
|
[app.common.data :as d]
|
||||||
|
[app.common.geom.proportions :as gpr]
|
||||||
|
[app.common.pages :as cp]
|
||||||
|
[app.common.pages.changes-builder :as pcb]
|
||||||
|
[app.common.pages.helpers :as cph]
|
||||||
|
[app.common.spec :as us]
|
||||||
|
[app.common.types.page :as csp]
|
||||||
|
[app.common.types.shape :as spec.shape]
|
||||||
|
[app.common.types.shape.interactions :as csi]
|
||||||
|
[app.common.uuid :as uuid]
|
||||||
|
[app.main.data.workspace.changes :as dch]
|
||||||
|
[app.main.data.workspace.edition :as dwe]
|
||||||
|
[app.main.data.workspace.selection :as dws]
|
||||||
|
[app.main.data.workspace.state-helpers :as wsh]
|
||||||
|
[app.main.streams :as ms]
|
||||||
|
[app.util.names :as un]
|
||||||
|
[beicon.core :as rx]
|
||||||
|
[cljs.spec.alpha :as s]
|
||||||
|
[potok.core :as ptk]))
|
||||||
|
|
||||||
|
(s/def ::shape-attrs ::spec.shape/shape-attrs)
|
||||||
|
|
||||||
|
(defn get-shape-layer-position
|
||||||
|
[objects selected attrs]
|
||||||
|
|
||||||
|
;; Calculate the frame over which we're drawing
|
||||||
|
(let [position @ms/mouse-position
|
||||||
|
frame-id (:frame-id attrs (cph/frame-id-by-position objects position))
|
||||||
|
shape (when-not (empty? selected)
|
||||||
|
(cph/get-base-shape objects selected))]
|
||||||
|
|
||||||
|
;; When no shapes has been selected or we're over a different frame
|
||||||
|
;; we add it as the latest shape of that frame
|
||||||
|
(if (or (not shape) (not= (:frame-id shape) frame-id))
|
||||||
|
[frame-id frame-id nil]
|
||||||
|
|
||||||
|
;; Otherwise, we add it to next to the selected shape
|
||||||
|
(let [index (cph/get-position-on-parent objects (:id shape))
|
||||||
|
{:keys [frame-id parent-id]} shape]
|
||||||
|
[frame-id parent-id (inc index)]))))
|
||||||
|
|
||||||
|
(defn make-new-shape
|
||||||
|
[attrs objects selected]
|
||||||
|
(let [default-attrs (if (= :frame (:type attrs))
|
||||||
|
cp/default-frame-attrs
|
||||||
|
cp/default-shape-attrs)
|
||||||
|
|
||||||
|
selected-non-frames
|
||||||
|
(into #{} (comp (map (d/getf objects))
|
||||||
|
(remove cph/frame-shape?))
|
||||||
|
selected)
|
||||||
|
|
||||||
|
[frame-id parent-id index]
|
||||||
|
(get-shape-layer-position objects selected-non-frames attrs)]
|
||||||
|
|
||||||
|
(-> (merge default-attrs attrs)
|
||||||
|
(gpr/setup-proportions)
|
||||||
|
(assoc :frame-id frame-id
|
||||||
|
:parent-id parent-id
|
||||||
|
:index index))))
|
||||||
|
|
||||||
|
(defn add-shape
|
||||||
|
([attrs]
|
||||||
|
(add-shape attrs {}))
|
||||||
|
|
||||||
|
([attrs {:keys [no-select?]}]
|
||||||
|
(us/verify ::shape-attrs attrs)
|
||||||
|
(ptk/reify ::add-shape
|
||||||
|
ptk/WatchEvent
|
||||||
|
(watch [it state _]
|
||||||
|
(let [page-id (:current-page-id state)
|
||||||
|
objects (wsh/lookup-page-objects state page-id)
|
||||||
|
selected (wsh/lookup-selected state)
|
||||||
|
|
||||||
|
id (or (:id attrs) (uuid/next))
|
||||||
|
name (-> objects
|
||||||
|
(un/retrieve-used-names)
|
||||||
|
(un/generate-unique-name (:name attrs)))
|
||||||
|
|
||||||
|
shape (make-new-shape
|
||||||
|
(assoc attrs :id id :name name)
|
||||||
|
objects
|
||||||
|
selected)
|
||||||
|
|
||||||
|
changes (-> (pcb/empty-changes it page-id)
|
||||||
|
(pcb/with-objects objects)
|
||||||
|
(pcb/add-object shape)
|
||||||
|
(cond-> (some? (:parent-id attrs))
|
||||||
|
(pcb/change-parent (:parent-id attrs) [shape])))]
|
||||||
|
|
||||||
|
(rx/concat
|
||||||
|
(rx/of (dch/commit-changes changes)
|
||||||
|
(when-not no-select?
|
||||||
|
(dws/select-shapes (d/ordered-set id))))
|
||||||
|
(when (= :text (:type attrs))
|
||||||
|
(->> (rx/of (dwe/start-edition-mode id))
|
||||||
|
(rx/observe-on :async)))))))))
|
||||||
|
|
||||||
|
(defn move-shapes-into-frame [frame-id shapes]
|
||||||
|
(ptk/reify ::move-shapes-into-frame
|
||||||
|
ptk/WatchEvent
|
||||||
|
(watch [it state _]
|
||||||
|
(let [page-id (:current-page-id state)
|
||||||
|
objects (wsh/lookup-page-objects state page-id)
|
||||||
|
|
||||||
|
to-move-shapes
|
||||||
|
(into []
|
||||||
|
(map (d/getf objects))
|
||||||
|
(reverse (cph/sort-z-index objects shapes)))
|
||||||
|
|
||||||
|
changes
|
||||||
|
(when (d/not-empty? to-move-shapes)
|
||||||
|
(-> (pcb/empty-changes it page-id)
|
||||||
|
(pcb/with-objects objects)
|
||||||
|
(pcb/change-parent frame-id to-move-shapes 0)))]
|
||||||
|
|
||||||
|
(if (some? changes)
|
||||||
|
(rx/of (dch/commit-changes changes))
|
||||||
|
(rx/empty))))))
|
||||||
|
|
||||||
|
(s/def ::set-of-uuid
|
||||||
|
(s/every ::us/uuid :kind set?))
|
||||||
|
|
||||||
|
(defn delete-shapes
|
||||||
|
[ids]
|
||||||
|
(us/assert ::set-of-uuid ids)
|
||||||
|
(ptk/reify ::delete-shapes
|
||||||
|
ptk/WatchEvent
|
||||||
|
(watch [it state _]
|
||||||
|
(let [page-id (:current-page-id state)
|
||||||
|
objects (wsh/lookup-page-objects state page-id)
|
||||||
|
page (wsh/lookup-page state page-id)
|
||||||
|
|
||||||
|
ids (cph/clean-loops objects ids)
|
||||||
|
|
||||||
|
groups-to-unmask
|
||||||
|
(reduce (fn [group-ids id]
|
||||||
|
;; When the shape to delete is the mask of a masked group,
|
||||||
|
;; the mask condition must be removed, and it must be
|
||||||
|
;; converted to a normal group.
|
||||||
|
(let [obj (get objects id)
|
||||||
|
parent (get objects (:parent-id obj))]
|
||||||
|
(if (and (:masked-group? parent)
|
||||||
|
(= id (first (:shapes parent))))
|
||||||
|
(conj group-ids (:id parent))
|
||||||
|
group-ids)))
|
||||||
|
#{}
|
||||||
|
ids)
|
||||||
|
|
||||||
|
interacting-shapes
|
||||||
|
(filter (fn [shape]
|
||||||
|
;; If any of the deleted shapes is the destination of
|
||||||
|
;; some interaction, this must be deleted, too.
|
||||||
|
(let [interactions (:interactions shape)]
|
||||||
|
(some #(and (csi/has-destination %)
|
||||||
|
(contains? ids (:destination %)))
|
||||||
|
interactions)))
|
||||||
|
(vals objects))
|
||||||
|
|
||||||
|
;; If any of the deleted shapes is a frame with guides
|
||||||
|
guides (into {} (map (juxt :id identity) (->> (get-in page [:options :guides])
|
||||||
|
(vals)
|
||||||
|
(filter #(not (contains? ids (:frame-id %)))))))
|
||||||
|
|
||||||
|
starting-flows
|
||||||
|
(filter (fn [flow]
|
||||||
|
;; If any of the deleted is a frame that starts a flow,
|
||||||
|
;; this must be deleted, too.
|
||||||
|
(contains? ids (:starting-frame flow)))
|
||||||
|
(-> page :options :flows))
|
||||||
|
|
||||||
|
all-parents
|
||||||
|
(reduce (fn [res id]
|
||||||
|
;; All parents of any deleted shape must be resized.
|
||||||
|
(into res (cph/get-parent-ids objects id)))
|
||||||
|
(d/ordered-set)
|
||||||
|
ids)
|
||||||
|
|
||||||
|
all-children
|
||||||
|
(->> ids ;; Children of deleted shapes must be also deleted.
|
||||||
|
(reduce (fn [res id]
|
||||||
|
(into res (cph/get-children-ids objects id)))
|
||||||
|
[])
|
||||||
|
(reverse)
|
||||||
|
(into (d/ordered-set)))
|
||||||
|
|
||||||
|
find-all-empty-parents (fn recursive-find-empty-parents [empty-parents]
|
||||||
|
(let [all-ids (into empty-parents ids)
|
||||||
|
empty-parents-xform
|
||||||
|
(comp
|
||||||
|
(map (fn [id] (get objects id)))
|
||||||
|
(map (fn [{:keys [shapes type] :as obj}]
|
||||||
|
(when (and (= :group type)
|
||||||
|
(zero? (count (remove #(contains? all-ids %) shapes))))
|
||||||
|
obj)))
|
||||||
|
(take-while some?)
|
||||||
|
(map :id))
|
||||||
|
calculated-empty-parents (into #{} empty-parents-xform all-parents)]
|
||||||
|
|
||||||
|
(if (= empty-parents calculated-empty-parents)
|
||||||
|
empty-parents
|
||||||
|
(recursive-find-empty-parents calculated-empty-parents))))
|
||||||
|
|
||||||
|
empty-parents
|
||||||
|
;; Any parent whose children are all deleted, must be deleted too.
|
||||||
|
(into (d/ordered-set) (find-all-empty-parents #{}))
|
||||||
|
|
||||||
|
changes (-> (pcb/empty-changes it page-id)
|
||||||
|
(pcb/with-page page)
|
||||||
|
(pcb/with-objects objects)
|
||||||
|
(pcb/set-page-option :guides guides)
|
||||||
|
(pcb/remove-objects all-children)
|
||||||
|
(pcb/remove-objects ids)
|
||||||
|
(pcb/remove-objects empty-parents)
|
||||||
|
(pcb/resize-parents all-parents)
|
||||||
|
(pcb/update-shapes groups-to-unmask
|
||||||
|
(fn [shape]
|
||||||
|
(assoc shape :masked-group? false)))
|
||||||
|
(pcb/update-shapes (map :id interacting-shapes)
|
||||||
|
(fn [shape]
|
||||||
|
(update shape :interactions
|
||||||
|
(fn [interactions]
|
||||||
|
(when interactions
|
||||||
|
(d/removev #(and (csi/has-destination %)
|
||||||
|
(contains? ids (:destination %)))
|
||||||
|
interactions))))))
|
||||||
|
(cond->
|
||||||
|
(seq starting-flows)
|
||||||
|
(pcb/update-page-option :flows (fn [flows]
|
||||||
|
(reduce #(csp/remove-flow %1 (:id %2))
|
||||||
|
flows
|
||||||
|
starting-flows)))))]
|
||||||
|
|
||||||
|
(rx/of (dch/commit-changes changes))))))
|
||||||
|
|
||||||
|
(defn- viewport-center
|
||||||
|
[state]
|
||||||
|
(let [{:keys [x y width height]} (get-in state [:workspace-local :vbox])]
|
||||||
|
[(+ x (/ width 2)) (+ y (/ height 2))]))
|
||||||
|
|
||||||
|
(defn create-and-add-shape
|
||||||
|
[type frame-x frame-y data]
|
||||||
|
(ptk/reify ::create-and-add-shape
|
||||||
|
ptk/WatchEvent
|
||||||
|
(watch [_ state _]
|
||||||
|
(prn ">>>create-")
|
||||||
|
(let [{:keys [width height]} data
|
||||||
|
|
||||||
|
[vbc-x vbc-y] (viewport-center state)
|
||||||
|
x (:x data (- vbc-x (/ width 2)))
|
||||||
|
y (:y data (- vbc-y (/ height 2)))
|
||||||
|
page-id (:current-page-id state)
|
||||||
|
frame-id (-> (wsh/lookup-page-objects state page-id)
|
||||||
|
(cph/frame-id-by-position {:x frame-x :y frame-y}))
|
||||||
|
shape (-> (cp/make-minimal-shape type)
|
||||||
|
(merge data)
|
||||||
|
(merge {:x x :y y})
|
||||||
|
(assoc :frame-id frame-id)
|
||||||
|
(cp/setup-rect-selrect))]
|
||||||
|
(rx/of (add-shape shape))))))
|
|
@ -17,9 +17,11 @@
|
||||||
[app.common.spec :refer [max-safe-int min-safe-int]]
|
[app.common.spec :refer [max-safe-int min-safe-int]]
|
||||||
[app.common.uuid :as uuid]
|
[app.common.uuid :as uuid]
|
||||||
[app.main.data.workspace.changes :as dch]
|
[app.main.data.workspace.changes :as dch]
|
||||||
[app.main.data.workspace.common :as dwc]
|
[app.main.data.workspace.selection :as dws]
|
||||||
|
[app.main.data.workspace.shapes :as dwsh]
|
||||||
[app.main.data.workspace.state-helpers :as wsh]
|
[app.main.data.workspace.state-helpers :as wsh]
|
||||||
[app.util.color :as uc]
|
[app.util.color :as uc]
|
||||||
|
[app.util.names :as un]
|
||||||
[app.util.path.parser :as upp]
|
[app.util.path.parser :as upp]
|
||||||
[app.util.svg :as usvg]
|
[app.util.svg :as usvg]
|
||||||
[beicon.core :as rx]
|
[beicon.core :as rx]
|
||||||
|
@ -358,7 +360,7 @@
|
||||||
(let [{:keys [tag attrs hidden]} element-data
|
(let [{:keys [tag attrs hidden]} element-data
|
||||||
attrs (usvg/format-styles attrs)
|
attrs (usvg/format-styles attrs)
|
||||||
element-data (cond-> element-data (map? element-data) (assoc :attrs attrs))
|
element-data (cond-> element-data (map? element-data) (assoc :attrs attrs))
|
||||||
name (dwc/generate-unique-name unames (or (:id attrs) (tag->name tag)))
|
name (un/generate-unique-name unames (or (:id attrs) (tag->name tag)))
|
||||||
att-refs (usvg/find-attr-references attrs)
|
att-refs (usvg/find-attr-references attrs)
|
||||||
references (usvg/find-def-references (:defs svg-data) att-refs)
|
references (usvg/find-def-references (:defs svg-data) att-refs)
|
||||||
|
|
||||||
|
@ -415,7 +417,7 @@
|
||||||
(if (some? shape)
|
(if (some? shape)
|
||||||
(let [shape-id (:id shape)
|
(let [shape-id (:id shape)
|
||||||
|
|
||||||
new-shape (dwc/make-new-shape shape objects selected)
|
new-shape (dwsh/make-new-shape shape objects selected)
|
||||||
changes (-> changes
|
changes (-> changes
|
||||||
(pcb/add-object new-shape)
|
(pcb/add-object new-shape)
|
||||||
(pcb/change-parent parent-id [new-shape] index))
|
(pcb/change-parent parent-id [new-shape] index))
|
||||||
|
@ -442,10 +444,10 @@
|
||||||
x (- x vb-x (/ vb-width 2))
|
x (- x vb-x (/ vb-width 2))
|
||||||
y (- y vb-y (/ vb-height 2))
|
y (- y vb-y (/ vb-height 2))
|
||||||
|
|
||||||
unames (dwc/retrieve-used-names objects)
|
unames (un/retrieve-used-names objects)
|
||||||
|
|
||||||
svg-name (->> (str/replace (:name svg-data) ".svg" "")
|
svg-name (->> (str/replace (:name svg-data) ".svg" "")
|
||||||
(dwc/generate-unique-name unames))
|
(un/generate-unique-name unames))
|
||||||
|
|
||||||
svg-data (-> svg-data
|
svg-data (-> svg-data
|
||||||
(assoc :x x
|
(assoc :x x
|
||||||
|
@ -482,7 +484,7 @@
|
||||||
(assoc :content (into [base-background-shape] (:content svg-data))))
|
(assoc :content (into [base-background-shape] (:content svg-data))))
|
||||||
|
|
||||||
;; Creates the root shape
|
;; Creates the root shape
|
||||||
new-shape (dwc/make-new-shape root-shape objects selected)
|
new-shape (dwsh/make-new-shape root-shape objects selected)
|
||||||
|
|
||||||
changes (-> (pcb/empty-changes it page-id)
|
changes (-> (pcb/empty-changes it page-id)
|
||||||
(pcb/with-objects objects)
|
(pcb/with-objects objects)
|
||||||
|
@ -506,7 +508,7 @@
|
||||||
vec))]
|
vec))]
|
||||||
|
|
||||||
(rx/of (dch/commit-changes changes)
|
(rx/of (dch/commit-changes changes)
|
||||||
(dwc/select-shapes (d/ordered-set root-id))))
|
(dws/select-shapes (d/ordered-set root-id))))
|
||||||
|
|
||||||
(catch :default e
|
(catch :default e
|
||||||
(.error js/console "Error SVG" e)
|
(.error js/console "Error SVG" e)
|
||||||
|
|
|
@ -17,6 +17,7 @@
|
||||||
[app.main.data.workspace.changes :as dch]
|
[app.main.data.workspace.changes :as dch]
|
||||||
[app.main.data.workspace.common :as dwc]
|
[app.main.data.workspace.common :as dwc]
|
||||||
[app.main.data.workspace.selection :as dws]
|
[app.main.data.workspace.selection :as dws]
|
||||||
|
[app.main.data.workspace.shapes :as dwsh]
|
||||||
[app.main.data.workspace.state-helpers :as wsh]
|
[app.main.data.workspace.state-helpers :as wsh]
|
||||||
[app.main.data.workspace.undo :as dwu]
|
[app.main.data.workspace.undo :as dwu]
|
||||||
[app.util.router :as rt]
|
[app.util.router :as rt]
|
||||||
|
@ -78,7 +79,7 @@
|
||||||
|
|
||||||
(when (some? id)
|
(when (some? id)
|
||||||
(rx/of (dws/deselect-shape id)
|
(rx/of (dws/deselect-shape id)
|
||||||
(dwc/delete-shapes #{id})))))))))
|
(dwsh/delete-shapes #{id})))))))))
|
||||||
|
|
||||||
(defn initialize-editor-state
|
(defn initialize-editor-state
|
||||||
[{:keys [id content] :as shape} decorator]
|
[{:keys [id content] :as shape} decorator]
|
||||||
|
|
|
@ -17,7 +17,7 @@
|
||||||
[app.common.pages.helpers :as cph]
|
[app.common.pages.helpers :as cph]
|
||||||
[app.common.spec :as us]
|
[app.common.spec :as us]
|
||||||
[app.main.data.workspace.changes :as dch]
|
[app.main.data.workspace.changes :as dch]
|
||||||
[app.main.data.workspace.common :as dwc]
|
[app.main.data.workspace.collapse :as dwc]
|
||||||
[app.main.data.workspace.guides :as dwg]
|
[app.main.data.workspace.guides :as dwg]
|
||||||
[app.main.data.workspace.selection :as dws]
|
[app.main.data.workspace.selection :as dws]
|
||||||
[app.main.data.workspace.state-helpers :as wsh]
|
[app.main.data.workspace.state-helpers :as wsh]
|
||||||
|
|
|
@ -11,7 +11,7 @@
|
||||||
[app.common.pages.helpers :as cph]
|
[app.common.pages.helpers :as cph]
|
||||||
[app.common.uuid :as uuid]
|
[app.common.uuid :as uuid]
|
||||||
[app.main.data.workspace :as dw]
|
[app.main.data.workspace :as dw]
|
||||||
[app.main.data.workspace.common :as dwc]
|
[app.main.data.workspace.collapse :as dwc]
|
||||||
[app.main.refs :as refs]
|
[app.main.refs :as refs]
|
||||||
[app.main.store :as st]
|
[app.main.store :as st]
|
||||||
[app.main.ui.components.shape-icon :as si]
|
[app.main.ui.components.shape-icon :as si]
|
||||||
|
|
|
@ -10,7 +10,7 @@
|
||||||
[app.common.data.macros :as dm]
|
[app.common.data.macros :as dm]
|
||||||
[app.common.text :as txt]
|
[app.common.text :as txt]
|
||||||
[app.main.data.workspace.colors :as dc]
|
[app.main.data.workspace.colors :as dc]
|
||||||
[app.main.data.workspace.common :as dwc]
|
[app.main.data.workspace.selection :as dws]
|
||||||
[app.main.store :as st]
|
[app.main.store :as st]
|
||||||
[app.main.ui.icons :as i]
|
[app.main.ui.icons :as i]
|
||||||
[app.main.ui.workspace.sidebar.options.rows.color-row :refer [color-row]]
|
[app.main.ui.workspace.sidebar.options.rows.color-row :refer [color-row]]
|
||||||
|
@ -148,7 +148,7 @@
|
||||||
(fn [color]
|
(fn [color]
|
||||||
(let [shapes-by-color (get @grouped-colors* color)
|
(let [shapes-by-color (get @grouped-colors* color)
|
||||||
ids (into (d/ordered-set) (map :shape-id) shapes-by-color)]
|
ids (into (d/ordered-set) (map :shape-id) shapes-by-color)]
|
||||||
(st/emit! (dwc/select-shapes ids)))))]
|
(st/emit! (dws/select-shapes ids)))))]
|
||||||
|
|
||||||
(mf/with-effect [grouped-colors]
|
(mf/with-effect [grouped-colors]
|
||||||
(reset! grouped-colors* grouped-colors))
|
(reset! grouped-colors* grouped-colors))
|
||||||
|
|
38
frontend/src/app/util/names.cljs
Normal file
38
frontend/src/app/util/names.cljs
Normal file
|
@ -0,0 +1,38 @@
|
||||||
|
;; This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
;; License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
;; file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
;;
|
||||||
|
;; Copyright (c) UXBOX Labs SL
|
||||||
|
|
||||||
|
(ns app.util.names
|
||||||
|
(:require
|
||||||
|
[app.common.data :as d]
|
||||||
|
[app.common.spec :as us]
|
||||||
|
[cljs.spec.alpha :as s]))
|
||||||
|
|
||||||
|
(s/def ::set-of-string (s/every string? :kind set?))
|
||||||
|
|
||||||
|
(defn- extract-numeric-suffix
|
||||||
|
[basename]
|
||||||
|
(if-let [[_ p1 p2] (re-find #"(.*)-([0-9]+)$" basename)]
|
||||||
|
[p1 (+ 1 (d/parse-integer p2))]
|
||||||
|
[basename 1]))
|
||||||
|
|
||||||
|
(defn retrieve-used-names
|
||||||
|
[objects]
|
||||||
|
(into #{} (comp (map :name) (remove nil?)) (vals objects)))
|
||||||
|
|
||||||
|
(defn generate-unique-name
|
||||||
|
"A unique name generator"
|
||||||
|
[used basename]
|
||||||
|
(s/assert ::set-of-string used)
|
||||||
|
(s/assert ::us/string basename)
|
||||||
|
(if-not (contains? used basename)
|
||||||
|
basename
|
||||||
|
(let [[prefix initial] (extract-numeric-suffix basename)]
|
||||||
|
(loop [counter initial]
|
||||||
|
(let [candidate (str prefix "-" counter)]
|
||||||
|
(if (contains? used candidate)
|
||||||
|
(recur (inc counter))
|
||||||
|
candidate))))))
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
[app.common.pages.helpers :as cph]
|
[app.common.pages.helpers :as cph]
|
||||||
[app.main.data.workspace :as dw]
|
[app.main.data.workspace :as dw]
|
||||||
[app.main.data.workspace.changes :as dch]
|
[app.main.data.workspace.changes :as dch]
|
||||||
[app.main.data.workspace.common :as dwc]
|
[app.main.data.workspace.shapes :as dwsh]
|
||||||
[app.main.data.workspace.libraries :as dwl]
|
[app.main.data.workspace.libraries :as dwl]
|
||||||
[app.main.data.workspace.libraries-helpers :as dwlh]
|
[app.main.data.workspace.libraries-helpers :as dwlh]
|
||||||
[app.main.data.workspace.state-helpers :as wsh]
|
[app.main.data.workspace.state-helpers :as wsh]
|
||||||
|
@ -192,7 +192,7 @@
|
||||||
|
|
||||||
(ptk/emit!
|
(ptk/emit!
|
||||||
store
|
store
|
||||||
(dwc/delete-shapes #{(:id shape1)})
|
(dwsh/delete-shapes #{(:id shape1)})
|
||||||
:the/end)))))
|
:the/end)))))
|
||||||
|
|
||||||
(t/deftest test-touched-children-move
|
(t/deftest test-touched-children-move
|
||||||
|
@ -767,7 +767,7 @@
|
||||||
|
|
||||||
(ptk/emit!
|
(ptk/emit!
|
||||||
store
|
store
|
||||||
(dwc/delete-shapes #{(:id shape1)})
|
(dwsh/delete-shapes #{(:id shape1)})
|
||||||
(dwl/reset-component (:id instance1))
|
(dwl/reset-component (:id instance1))
|
||||||
:the/end)))))
|
:the/end)))))
|
||||||
|
|
||||||
|
@ -1538,7 +1538,7 @@
|
||||||
|
|
||||||
(ptk/emit!
|
(ptk/emit!
|
||||||
store
|
store
|
||||||
(dwc/delete-shapes #{(:id shape1)})
|
(dwsh/delete-shapes #{(:id shape1)})
|
||||||
(dwl/update-component-sync (:id instance1) (:id file))
|
(dwl/update-component-sync (:id instance1) (:id file))
|
||||||
:the/end)))))
|
:the/end)))))
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue