diff --git a/common/src/app/common/geom/shapes/bounds.cljc b/common/src/app/common/geom/shapes/bounds.cljc index 012183e9b..83a1b25f5 100644 --- a/common/src/app/common/geom/shapes/bounds.cljc +++ b/common/src/app/common/geom/shapes/bounds.cljc @@ -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 (:require [app.common.data :as d] diff --git a/frontend/src/app/main/data/users.cljs b/frontend/src/app/main/data/users.cljs index eee3b49cc..bb091334c 100644 --- a/frontend/src/app/main/data/users.cljs +++ b/frontend/src/app/main/data/users.cljs @@ -455,7 +455,7 @@ (->> users (d/index-by :id) (assoc state :file-comments-users)))] - (ptk/reify ::fetch-team-users + (ptk/reify ::fetch-file-comments-users ptk/WatchEvent (watch [_ state _] (let [share-id (-> state :viewer-local :share-id)] diff --git a/frontend/src/app/main/data/workspace.cljs b/frontend/src/app/main/data/workspace.cljs index d146c13e7..8d2abf1c8 100644 --- a/frontend/src/app/main/data/workspace.cljs +++ b/frontend/src/app/main/data/workspace.cljs @@ -28,8 +28,10 @@ [app.main.data.users :as du] [app.main.data.workspace.bool :as dwb] [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.drawing :as dwd] + [app.main.data.workspace.edition :as dwe] [app.main.data.workspace.fix-bool-contents :as fbc] [app.main.data.workspace.groups :as dwg] [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.persistence :as dwp] [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.thumbnails :as dwth] [app.main.data.workspace.transforms :as dwt] @@ -54,6 +57,7 @@ [app.util.globals :as ug] [app.util.http :as http] [app.util.i18n :as i18n] + [app.util.names :as un] [app.util.router :as rt] [app.util.timers :as tm] [app.util.webapi :as wapi] @@ -264,8 +268,8 @@ ptk/WatchEvent (watch [it state _] (let [pages (get-in state [:workspace-data :pages-index]) - unames (dwc/retrieve-used-names pages) - name (dwc/generate-unique-name unames "Page-1") + unames (un/retrieve-used-names pages) + name (un/generate-unique-name unames "Page-1") changes (-> (pcb/empty-changes it) (pcb/add-empty-page id name))] @@ -279,9 +283,9 @@ (watch [it state _] (let [id (uuid/next) 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]) - name (dwc/generate-unique-name unames (:name page)) + name (un/generate-unique-name unames (:name page)) no_thumbnails_objects (->> (:objects page) (d/mapm (fn [_ val] (dissoc val :use-for-thumbnail?)))) @@ -577,7 +581,7 @@ hover-guides (get-in state [:workspace-guides :hover])] (cond (d/not-empty? selected) - (rx/of (dwc/delete-shapes selected) + (rx/of (dwsh/delete-shapes selected) (dws/deselect-all)) (d/not-empty? hover-guides) @@ -795,7 +799,7 @@ ids)] (rx/of (dch/commit-changes changes) - (dwc/expand-collapse parent-id)))))) + (dwco/expand-collapse parent-id)))))) (defn relocate-selected-shapes [parent-id to-index] @@ -820,15 +824,15 @@ (case type :text - (rx/of (dwc/start-edition-mode id)) + (rx/of (dwe/start-edition-mode id)) (: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 nil - (rx/of (dwc/start-edition-mode id) + (rx/of (dwe/start-edition-mode id) (dwdp/start-path-edit id))))))))) @@ -1548,7 +1552,7 @@ (into (d/ordered-set)))] (rx/of (dch/commit-changes changes) - (dwc/select-shapes selected))))] + (dws/select-shapes selected))))] (ptk/reify ::paste-shape ptk/WatchEvent @@ -1597,7 +1601,7 @@ :content (as-content text)})] (rx/of (dwu/start-undo-transaction) (dws/deselect-all) - (dwc/add-shape shape) + (dwsh/add-shape shape) (dwu/commit-undo-transaction)))))) ;; TODO: why not implement it in terms of upload-media-workspace? @@ -1682,9 +1686,9 @@ (cp/setup-rect-selrect))] (rx/of (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)))))))) @@ -1707,10 +1711,10 @@ (dm/export dwly/set-opacity) ;; Common -(dm/export dwc/add-shape) -(dm/export dwc/clear-edition-mode) -(dm/export dwc/select-shapes) -(dm/export dwc/start-edition-mode) +(dm/export dwsh/add-shape) +(dm/export dwe/clear-edition-mode) +(dm/export dws/select-shapes) +(dm/export dwe/start-edition-mode) ;; Drawing (dm/export dwd/select-for-drawing) diff --git a/frontend/src/app/main/data/workspace/bool.cljs b/frontend/src/app/main/data/workspace/bool.cljs index 2c316d294..771c06bb2 100644 --- a/frontend/src/app/main/data/workspace/bool.cljs +++ b/frontend/src/app/main/data/workspace/bool.cljs @@ -13,8 +13,9 @@ [app.common.path.shapes-to-path :as stp] [app.common.uuid :as uuid] [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.util.names :as un] [beicon.core :as rx] [cuerdas.core :as str] [potok.core :as ptk])) @@ -89,8 +90,8 @@ (let [page-id (:current-page-id state) objects (wsh/lookup-page-objects state) base-name (-> bool-type d/name str/capital (str "-1")) - name (-> (dwc/retrieve-used-names objects) - (dwc/generate-unique-name base-name)) + name (-> (un/retrieve-used-names objects) + (un/generate-unique-name base-name)) shapes (selected-shapes state)] (when-not (empty? shapes) @@ -101,7 +102,7 @@ (pcb/add-object boolean-data {:index index}) (pcb/change-parent shape-id shapes))] (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 [shape-id bool-type] diff --git a/frontend/src/app/main/data/workspace/collapse.cljs b/frontend/src/app/main/data/workspace/collapse.cljs new file mode 100644 index 000000000..4c23b1747 --- /dev/null +++ b/frontend/src/app/main/data/workspace/collapse.cljs @@ -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)))) + diff --git a/frontend/src/app/main/data/workspace/common.cljs b/frontend/src/app/main/data/workspace/common.cljs index 8eddc4758..d9671bfed 100644 --- a/frontend/src/app/main/data/workspace/common.cljs +++ b/frontend/src/app/main/data/workspace/common.cljs @@ -6,33 +6,16 @@ (ns app.main.data.workspace.common (:require - [app.common.data :as d] - [app.common.geom.proportions :as gpr] [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.state-helpers :as wsh] [app.main.data.workspace.undo :as dwu] - [app.main.streams :as ms] [app.main.worker :as uw] [beicon.core :as rx] - [cljs.spec.alpha :as s] [potok.core :as ptk])) ;; Change this to :info :debug or :trace to debug this module (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? "Check if the state is properly intialized in a workspace. This means it has the `:current-page-id` and `:current-file-id` properly set." @@ -56,57 +39,6 @@ (->> (uw/ask! msg) (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 ;; a circular dependency with `src/app/main/data/workspace/changes.cljs` (def undo @@ -177,315 +109,3 @@ :origin it :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)))))) diff --git a/frontend/src/app/main/data/workspace/drawing/common.cljs b/frontend/src/app/main/data/workspace/drawing/common.cljs index 1539b398c..03f956aa5 100644 --- a/frontend/src/app/main/data/workspace/drawing/common.cljs +++ b/frontend/src/app/main/data/workspace/drawing/common.cljs @@ -11,7 +11,7 @@ [app.common.math :as mth] [app.common.pages :as cp] [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.undo :as dwu] [app.main.worker :as uw] @@ -66,7 +66,7 @@ (rx/of (dwu/start-undo-transaction)) (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)) (->> (uw/ask! {:cmd :selection/query @@ -75,7 +75,7 @@ :include-frames? true :full-frame? true}) (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))))) ;; Delay so the mouse event can read the drawing state diff --git a/frontend/src/app/main/data/workspace/edition.cljs b/frontend/src/app/main/data/workspace/edition.cljs new file mode 100644 index 000000000..b5514b6f3 --- /dev/null +++ b/frontend/src/app/main/data/workspace/edition.cljs @@ -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))))))) diff --git a/frontend/src/app/main/data/workspace/groups.cljs b/frontend/src/app/main/data/workspace/groups.cljs index 3dd032e0c..2b315fc5e 100644 --- a/frontend/src/app/main/data/workspace/groups.cljs +++ b/frontend/src/app/main/data/workspace/groups.cljs @@ -12,8 +12,9 @@ [app.common.pages.changes-builder :as pcb] [app.common.pages.helpers :as cph] [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.util.names :as un] [beicon.core :as rx] [potok.core :as ptk])) @@ -70,8 +71,8 @@ (= (count shapes) 1) (= (:type (first shapes)) :group)) (:name (first shapes)) - (-> (dwc/retrieve-used-names objects) - (dwc/generate-unique-name base-name))) + (-> (un/retrieve-used-names objects) + (un/generate-unique-name base-name))) selrect (gsh/selection-rect shapes) group (-> (cp/make-minimal-group frame-id selrect gname) @@ -142,7 +143,7 @@ (let [[group changes] (prepare-create-group it objects page-id shapes "Group-1" false)] (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 (ptk/reify ::ungroup-selected @@ -203,7 +204,7 @@ (pcb/resize-parents [(:id group)]))] (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 (ptk/reify ::unmask-group diff --git a/frontend/src/app/main/data/workspace/interactions.cljs b/frontend/src/app/main/data/workspace/interactions.cljs index 5ea0d8b83..40c6a80f9 100644 --- a/frontend/src/app/main/data/workspace/interactions.cljs +++ b/frontend/src/app/main/data/workspace/interactions.cljs @@ -15,9 +15,9 @@ [app.common.types.shape.interactions :as ctsi] [app.common.uuid :as uuid] [app.main.data.workspace.changes :as dch] - [app.main.data.workspace.common :as dwc] [app.main.data.workspace.state-helpers :as wsh] [app.main.streams :as ms] + [app.util.names :as un] [beicon.core :as rx] [potok.core :as ptk])) @@ -32,7 +32,7 @@ flows (get-in page [:options :flows] []) unames (into #{} (map :name flows)) - name (dwc/generate-unique-name unames "Flow-1") + name (un/generate-unique-name unames "Flow-1") new-flow {:id (uuid/next) :name name diff --git a/frontend/src/app/main/data/workspace/libraries.cljs b/frontend/src/app/main/data/workspace/libraries.cljs index 47c2aa703..7663a0585 100644 --- a/frontend/src/app/main/data/workspace/libraries.cljs +++ b/frontend/src/app/main/data/workspace/libraries.cljs @@ -22,14 +22,15 @@ [app.main.data.events :as ev] [app.main.data.messages :as dm] [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.libraries-helpers :as dwlh] + [app.main.data.workspace.selection :as dws] [app.main.data.workspace.state-helpers :as wsh] [app.main.data.workspace.undo :as dwu] [app.main.repo :as rp] [app.main.store :as st] [app.util.i18n :refer [tr]] + [app.util.names :as un] [app.util.router :as rt] [app.util.time :as dt] [beicon.core :as rx] @@ -295,7 +296,7 @@ (dwlh/generate-add-component it shapes objects page-id file-id)] (when-not (empty? (:redo-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 "Add a new component to current file library, from the currently selected shapes. @@ -351,7 +352,7 @@ component (cph/get-component libraries id) all-components (-> state :workspace-data :components vals) 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] (dwlh/duplicate-component component) @@ -401,7 +402,7 @@ page libraries)] (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 "Remove all references to components in the shape with the given id, diff --git a/frontend/src/app/main/data/workspace/libraries_helpers.cljs b/frontend/src/app/main/data/workspace/libraries_helpers.cljs index c95763d98..a1e50c89f 100644 --- a/frontend/src/app/main/data/workspace/libraries_helpers.cljs +++ b/frontend/src/app/main/data/workspace/libraries_helpers.cljs @@ -16,9 +16,9 @@ [app.common.spec :as us] [app.common.text :as txt] [app.common.types.color :as ctc] - [app.main.data.workspace.common :as dwc] [app.main.data.workspace.groups :as dwg] [app.main.data.workspace.state-helpers :as wsh] + [app.util.names :as un] [cljs.spec.alpha :as s] [clojure.set :as set])) @@ -144,13 +144,13 @@ delta (gpt/subtract position orig-pos) 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)) update-new-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)) (vswap! unames conj new-name)) diff --git a/frontend/src/app/main/data/workspace/media.cljs b/frontend/src/app/main/data/workspace/media.cljs index 777dc8d8d..ce0959c33 100644 --- a/frontend/src/app/main/data/workspace/media.cljs +++ b/frontend/src/app/main/data/workspace/media.cljs @@ -10,8 +10,8 @@ [app.common.spec :as us] [app.main.data.media :as dmm] [app.main.data.messages :as dm] - [app.main.data.workspace.common :as dwc] [app.main.data.workspace.libraries :as dwl] + [app.main.data.workspace.shapes :as dwsh] [app.main.data.workspace.svg-upload :as svg] [app.main.repo :as rp] [app.main.store :as st] @@ -72,7 +72,7 @@ :height height :mtype mtype :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 [svg-data file-id position] diff --git a/frontend/src/app/main/data/workspace/path/drawing.cljs b/frontend/src/app/main/data/workspace/path/drawing.cljs index 5c46ecc45..ff61c094a 100644 --- a/frontend/src/app/main/data/workspace/path/drawing.cljs +++ b/frontend/src/app/main/data/workspace/path/drawing.cljs @@ -13,8 +13,8 @@ [app.common.path.shapes-to-path :as upsp] [app.common.spec :as us] [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.edition :as dwe] [app.main.data.workspace.path.changes :as changes] [app.main.data.workspace.path.common :as common] [app.main.data.workspace.path.helpers :as helpers] @@ -276,7 +276,7 @@ (watch [_ _ _] (rx/of (setup-frame-path) (dwdc/handle-finish-drawing) - (dwc/start-edition-mode shape-id) + (dwe/start-edition-mode shape-id) (change-edit-mode :draw))))) (defn handle-new-shape diff --git a/frontend/src/app/main/data/workspace/path/edition.cljs b/frontend/src/app/main/data/workspace/path/edition.cljs index fa8c00f51..b3b0b6812 100644 --- a/frontend/src/app/main/data/workspace/path/edition.cljs +++ b/frontend/src/app/main/data/workspace/path/edition.cljs @@ -13,7 +13,7 @@ [app.common.path.shapes-to-path :as upsp] [app.common.path.subpaths :as ups] [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.drawing :as drawing] [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)] (if (empty? new-content) (rx/of (dch/commit-changes changes) - dwc/clear-edition-mode) + dwe/clear-edition-mode) (rx/of (dch/commit-changes changes) (selection/update-selection point-change) (fn [state] (update-in state [:workspace-local :edit-path id] dissoc :content-modifiers :moving-nodes :moving-handler)))))))))) diff --git a/frontend/src/app/main/data/workspace/path/tools.cljs b/frontend/src/app/main/data/workspace/path/tools.cljs index 71a686662..3338c0c9e 100644 --- a/frontend/src/app/main/data/workspace/path/tools.cljs +++ b/frontend/src/app/main/data/workspace/path/tools.cljs @@ -9,7 +9,7 @@ [app.common.path.shapes-to-path :as upsp] [app.common.path.subpaths :as ups] [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.state :as st] [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/commit-changes changes) (when (empty? new-content) - dwc/clear-edition-mode)))))))))) + dwe/clear-edition-mode)))))))))) (defn make-corner ([] diff --git a/frontend/src/app/main/data/workspace/selection.cljs b/frontend/src/app/main/data/workspace/selection.cljs index d65dfcb42..777a5d49c 100644 --- a/frontend/src/app/main/data/workspace/selection.cljs +++ b/frontend/src/app/main/data/workspace/selection.cljs @@ -19,13 +19,14 @@ [app.common.uuid :as uuid] [app.main.data.modal :as md] [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.thumbnails :as dwt] [app.main.data.workspace.zoom :as dwz] [app.main.refs :as refs] [app.main.streams :as ms] [app.main.worker :as uw] + [app.util.names :as un] [beicon.core :as rx] [cljs.spec.alpha :as s] [clojure.set :as set] @@ -41,6 +42,8 @@ (s/def ::set-of-string (s/every string? :kind set?)) +(defn interrupt? [e] (= e :interrupt)) + ;; --- Selection Rect (declare select-shapes-by-current-selrect) @@ -59,7 +62,7 @@ ptk/WatchEvent (watch [_ state stream] (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?)) init-selrect @@ -281,7 +284,7 @@ move to the desired position, and recalculate parents and frames as needed." [all-objects page ids delta it] (let [shapes (map (d/getf all-objects) ids) - unames (volatile! (dwc/retrieve-used-names (:objects page))) + unames (volatile! (un/retrieve-used-names (:objects page))) update-unames! (fn [new-name] (vswap! unames conj new-name)) all-ids (reduce #(into %1 (cons %2 (cph/get-children-ids all-objects %2))) (d/ordered-set) ids) ids-map (into {} (map #(vector % (uuid/next))) all-ids) @@ -316,7 +319,7 @@ (defn- prepare-duplicate-frame-change [changes objects page unames update-unames! ids-map obj delta] (let [new-id (ids-map (:id obj)) - frame-name (dwc/generate-unique-name @unames (:name obj)) + frame-name (un/generate-unique-name @unames (:name obj)) _ (update-unames! frame-name) new-frame (-> obj @@ -351,7 +354,7 @@ (if (some? obj) (let [new-id (ids-map (:id obj)) 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) new-obj (-> obj @@ -392,7 +395,7 @@ (let [update-flows (fn [flows] (reduce (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) new-flow {:id (uuid/next) :name name diff --git a/frontend/src/app/main/data/workspace/shapes.cljs b/frontend/src/app/main/data/workspace/shapes.cljs new file mode 100644 index 000000000..9694db988 --- /dev/null +++ b/frontend/src/app/main/data/workspace/shapes.cljs @@ -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)))))) diff --git a/frontend/src/app/main/data/workspace/svg_upload.cljs b/frontend/src/app/main/data/workspace/svg_upload.cljs index 0424e7390..3c830bd59 100644 --- a/frontend/src/app/main/data/workspace/svg_upload.cljs +++ b/frontend/src/app/main/data/workspace/svg_upload.cljs @@ -17,9 +17,11 @@ [app.common.spec :refer [max-safe-int min-safe-int]] [app.common.uuid :as uuid] [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.util.color :as uc] + [app.util.names :as un] [app.util.path.parser :as upp] [app.util.svg :as usvg] [beicon.core :as rx] @@ -358,7 +360,7 @@ (let [{:keys [tag attrs hidden]} element-data attrs (usvg/format-styles attrs) element-data (cond-> element-data (map? element-data) (assoc :attrs attrs)) - name (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) references (usvg/find-def-references (:defs svg-data) att-refs) @@ -415,7 +417,7 @@ (if (some? 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 (pcb/add-object new-shape) (pcb/change-parent parent-id [new-shape] index)) @@ -442,10 +444,10 @@ x (- x vb-x (/ vb-width 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" "") - (dwc/generate-unique-name unames)) + (un/generate-unique-name unames)) svg-data (-> svg-data (assoc :x x @@ -482,7 +484,7 @@ (assoc :content (into [base-background-shape] (:content svg-data)))) ;; 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) (pcb/with-objects objects) @@ -506,7 +508,7 @@ vec))] (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 (.error js/console "Error SVG" e) diff --git a/frontend/src/app/main/data/workspace/texts.cljs b/frontend/src/app/main/data/workspace/texts.cljs index 30637d292..174464218 100644 --- a/frontend/src/app/main/data/workspace/texts.cljs +++ b/frontend/src/app/main/data/workspace/texts.cljs @@ -17,6 +17,7 @@ [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.undo :as dwu] [app.util.router :as rt] @@ -78,7 +79,7 @@ (when (some? id) (rx/of (dws/deselect-shape id) - (dwc/delete-shapes #{id}))))))))) + (dwsh/delete-shapes #{id}))))))))) (defn initialize-editor-state [{:keys [id content] :as shape} decorator] diff --git a/frontend/src/app/main/data/workspace/transforms.cljs b/frontend/src/app/main/data/workspace/transforms.cljs index ffd31b1fd..4d87ff0f6 100644 --- a/frontend/src/app/main/data/workspace/transforms.cljs +++ b/frontend/src/app/main/data/workspace/transforms.cljs @@ -17,7 +17,7 @@ [app.common.pages.helpers :as cph] [app.common.spec :as us] [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.selection :as dws] [app.main.data.workspace.state-helpers :as wsh] diff --git a/frontend/src/app/main/ui/workspace/sidebar/layers.cljs b/frontend/src/app/main/ui/workspace/sidebar/layers.cljs index 6b3132217..05ad9bdfd 100644 --- a/frontend/src/app/main/ui/workspace/sidebar/layers.cljs +++ b/frontend/src/app/main/ui/workspace/sidebar/layers.cljs @@ -11,7 +11,7 @@ [app.common.pages.helpers :as cph] [app.common.uuid :as uuid] [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.store :as st] [app.main.ui.components.shape-icon :as si] diff --git a/frontend/src/app/main/ui/workspace/sidebar/options/menus/color_selection.cljs b/frontend/src/app/main/ui/workspace/sidebar/options/menus/color_selection.cljs index 234c7cc06..95951ac5d 100644 --- a/frontend/src/app/main/ui/workspace/sidebar/options/menus/color_selection.cljs +++ b/frontend/src/app/main/ui/workspace/sidebar/options/menus/color_selection.cljs @@ -10,7 +10,7 @@ [app.common.data.macros :as dm] [app.common.text :as txt] [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.ui.icons :as i] [app.main.ui.workspace.sidebar.options.rows.color-row :refer [color-row]] @@ -148,7 +148,7 @@ (fn [color] (let [shapes-by-color (get @grouped-colors* 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] (reset! grouped-colors* grouped-colors)) diff --git a/frontend/src/app/util/names.cljs b/frontend/src/app/util/names.cljs new file mode 100644 index 000000000..6a2288fcd --- /dev/null +++ b/frontend/src/app/util/names.cljs @@ -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)))))) + diff --git a/frontend/test/app/components_sync_test.cljs b/frontend/test/app/components_sync_test.cljs index a34af0f16..b3d754a37 100644 --- a/frontend/test/app/components_sync_test.cljs +++ b/frontend/test/app/components_sync_test.cljs @@ -6,7 +6,7 @@ [app.common.pages.helpers :as cph] [app.main.data.workspace :as dw] [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-helpers :as dwlh] [app.main.data.workspace.state-helpers :as wsh] @@ -192,7 +192,7 @@ (ptk/emit! store - (dwc/delete-shapes #{(:id shape1)}) + (dwsh/delete-shapes #{(:id shape1)}) :the/end))))) (t/deftest test-touched-children-move @@ -767,7 +767,7 @@ (ptk/emit! store - (dwc/delete-shapes #{(:id shape1)}) + (dwsh/delete-shapes #{(:id shape1)}) (dwl/reset-component (:id instance1)) :the/end))))) @@ -1538,7 +1538,7 @@ (ptk/emit! store - (dwc/delete-shapes #{(:id shape1)}) + (dwsh/delete-shapes #{(:id shape1)}) (dwl/update-component-sync (:id instance1) (:id file)) :the/end)))))