diff --git a/frontend/src/app/main/data/workspace/svg_upload.cljs b/frontend/src/app/main/data/workspace/svg_upload.cljs index ea6b4a0723..96d7845ea7 100644 --- a/frontend/src/app/main/data/workspace/svg_upload.cljs +++ b/frontend/src/app/main/data/workspace/svg_upload.cljs @@ -18,7 +18,7 @@ [beicon.core :as rx] [cuerdas.core :as str] [potok.core :as ptk] - + [app.util.svg :as usvg] [app.util.geom.path :as ugp])) (defn- svg-dimensions [data] @@ -30,37 +30,6 @@ height (d/parse-integer height-str)] [width height])) - -(defn clean-attrs - "Transforms attributes to their react equivalent" - [attrs] - (letfn [(transform-key [key] - (-> (name key) - (str/replace ":" "-") - (str/camel) - (keyword))) - - (format-styles [style-str] - (->> (str/split style-str ";") - (map str/trim) - (map #(str/split % ":")) - (group-by first) - (map (fn [[key val]] - (vector - (transform-key key) - (second (first val))))) - (into {}))) - - (map-fn [[key val]] - (cond - (= key :class) [:className val] - (= key :style) [key (format-styles val)] - :else (vector (transform-key key) val)))] - - (->> attrs - (map map-fn) - (into {})))) - (defn tag-name [{:keys [tag]}] (cond (string? tag) tag (keyword? tag) (name tag) @@ -103,7 +72,7 @@ :height height :x x :y y - :content (if (map? data) (update data :attrs clean-attrs) data)} + :content (if (map? data) (update data :attrs usvg/clean-attrs) data)} (gsh/setup-selrect))) (defn parse-path [name frame-id {:keys [attrs] :as data}] diff --git a/frontend/src/app/main/data/workspace/texts.cljs b/frontend/src/app/main/data/workspace/texts.cljs index c3ce7b8315..7e0174599c 100644 --- a/frontend/src/app/main/data/workspace/texts.cljs +++ b/frontend/src/app/main/data/workspace/texts.cljs @@ -228,55 +228,56 @@ (let [page-id (:current-page-id state) objects0 (get-in state [:workspace-file :data :pages-index page-id :objects]) - objects1 (get-in state [:workspace-data :pages-index page-id :objects]) + objects1 (get-in state [:workspace-data :pages-index page-id :objects])] + (if-not (every? #(contains? objects1(first %)) changes) + (rx/empty) + (let [change-text-shape + (fn [objects [id [new-width new-height]]] + (when (contains? objects id) + (let [shape (get objects id) + {:keys [selrect grow-type overflow-text]} (gsh/transform-shape shape) + {shape-width :width shape-height :height} selrect - change-text-shape - (fn [objects [id [new-width new-height]]] + modifier-width (gsh/resize-modifiers shape :width new-width) + modifier-height (gsh/resize-modifiers shape :height new-height) - (let [shape (get objects id) - {:keys [selrect grow-type overflow-text]} (gsh/transform-shape shape) - {shape-width :width shape-height :height} selrect + shape (cond-> shape + (and overflow-text (not= :fixed grow-type)) + (assoc :overflow-text false) - modifier-width (gsh/resize-modifiers shape :width new-width) - modifier-height (gsh/resize-modifiers shape :height new-height) + (and (= :fixed grow-type) (not overflow-text) (> new-height shape-height)) + (assoc :overflow-text true) - shape (cond-> shape - (and overflow-text (not= :fixed grow-type)) - (assoc :overflow-text false) + (and (= :fixed grow-type) overflow-text (<= new-height shape-height)) + (assoc :overflow-text true) - (and (= :fixed grow-type) (not overflow-text) (> new-height shape-height)) - (assoc :overflow-text true) + (and (not-changed? shape-width new-width) (= grow-type :auto-width)) + (-> (assoc :modifiers modifier-width) + (gsh/transform-shape)) - (and (= :fixed grow-type) overflow-text (<= new-height shape-height)) - (assoc :overflow-text true) + (and (not-changed? shape-height new-height) + (or (= grow-type :auto-height) (= grow-type :auto-width))) + (-> (assoc :modifiers modifier-height) + (gsh/transform-shape)))] + (assoc objects id shape)))) - (and (not-changed? shape-width new-width) (= grow-type :auto-width)) - (-> (assoc :modifiers modifier-width) - (gsh/transform-shape)) + undo-transaction (get-in state [:workspace-undo :transaction]) + objects2 (->> changes (reduce change-text-shape objects1)) - (and (not-changed? shape-height new-height) - (or (= grow-type :auto-height) (= grow-type :auto-width))) - (-> (assoc :modifiers modifier-height) - (gsh/transform-shape)))] - (assoc objects id shape))) + regchg {:type :reg-objects + :page-id page-id + :shapes (vec (keys changes))} - undo-transaction (get-in state [:workspace-undo :transaction]) - objects2 (->> changes (reduce change-text-shape objects1)) + rchanges (dwc/generate-changes page-id objects1 objects2) + uchanges (dwc/generate-changes page-id objects2 objects0)] - regchg {:type :reg-objects - :page-id page-id - :shapes (vec (keys changes))} - - rchanges (dwc/generate-changes page-id objects1 objects2) - uchanges (dwc/generate-changes page-id objects2 objects0)] - - (if (seq rchanges) - (rx/concat - (when-not undo-transaction - (rx/of (dwc/start-undo-transaction))) - (rx/of (dwc/commit-changes (conj rchanges regchg) (conj uchanges regchg) {:commit-local? true})) - (when-not undo-transaction - (rx/of (dwc/discard-undo-transaction))))))))) + (if (seq rchanges) + (rx/concat + (when-not undo-transaction + (rx/of (dwc/start-undo-transaction))) + (rx/of (dwc/commit-changes (conj rchanges regchg) (conj uchanges regchg) {:commit-local? true})) + (when-not undo-transaction + (rx/of (dwc/discard-undo-transaction))))))))))) ;; When a resize-event arrives we start "buffering" for a time ;; after that time we invoke `resize-text-batch` with all the changes @@ -292,22 +293,33 @@ ptk/WatchEvent (watch [_ state stream] (let [;; This stream aggregates the events of "resizing" - resize-events (rx/merge - (->> (rx/of (resize-text id new-width new-height))) - (->> stream (rx/filter (ptk/type? ::resize-text)))) + resize-events + (rx/merge + (->> (rx/of (resize-text id new-width new-height))) + (->> stream (rx/filter (ptk/type? ::resize-text)))) ;; Stop buffering after time without resizes - stop-buffer (->> resize-events (rx/debounce 100))] + stop-buffer (->> resize-events (rx/debounce 100)) + + ;; Agregates the resizes so only send the resize when the sizes are stable + resize-batch + (->> resize-events + (rx/take-until stop-buffer) + (rx/reduce (fn [acc event] + (assoc acc (:id @event) [(:width @event) (:height @event)])) + {id [new-width new-height]}) + (rx/map #(resize-text-batch %))) + + ;; This stream retrieves the changes of page so we cancel the agregation + change-page + (->> stream + (rx/filter (ptk/type? :app.main.data.workspace/finalize-page)) + (rx/take 1) + (rx/ignore))] (if-not (::handling-texts state) (->> (rx/concat (rx/of #(assoc % ::handling-texts true)) - (->> resize-events - (rx/take-until stop-buffer) - (rx/reduce (fn [acc event] - (assoc acc (:id @event) [(:width @event) (:height @event)])) - {id [new-width new-height]}) - (rx/map #(resize-text-batch %))) - + (rx/race resize-batch change-page) (rx/of #(dissoc % ::handling-texts)))) (rx/empty)))))) diff --git a/frontend/src/app/main/ui/handoff/right_sidebar.cljs b/frontend/src/app/main/ui/handoff/right_sidebar.cljs index ce9abf05cb..cce8f4f102 100644 --- a/frontend/src/app/main/ui/handoff/right_sidebar.cljs +++ b/frontend/src/app/main/ui/handoff/right_sidebar.cljs @@ -29,14 +29,14 @@ (mapv resolve-shape selected)))] #(l/derived selected->shapes st/state))) - (mf/defc right-sidebar [{:keys [frame page-id file-id]}] (let [expanded (mf/use-state false) locale (mf/deref i18n/locale) section (mf/use-state :info #_:code) selected-ref (mf/use-memo (make-selected-shapes-iref)) - shapes (mf/deref selected-ref)] + shapes (mf/deref selected-ref) + selected-type (-> shapes first (:type :not-found))] [:aside.settings-bar.settings-bar-right {:class (when @expanded "expanded")} [:div.settings-bar-inside (when (seq shapes) @@ -49,7 +49,7 @@ [:* [:span.tool-window-bar-icon [:& element-icon {:shape (-> shapes first)}]] - [:span.tool-window-bar-title (->> shapes first :type name (str "handoff.tabs.code.selected.") (t locale))]]) + [:span.tool-window-bar-title (->> selected-type name (str "handoff.tabs.code.selected.") (t locale))]]) ] [:div.tool-window-content [:& tab-container {:on-change-tab #(do diff --git a/frontend/src/app/main/ui/handoff/selection_feedback.cljs b/frontend/src/app/main/ui/handoff/selection_feedback.cljs index 371134d568..a382b26a4e 100644 --- a/frontend/src/app/main/ui/handoff/selection_feedback.cljs +++ b/frontend/src/app/main/ui/handoff/selection_feedback.cljs @@ -40,7 +40,7 @@ (let [selected (get-in state [:viewer-local :selected]) objects (get-in state [:viewer-data :page :objects]) resolve-shape #(get objects %)] - (mapv resolve-shape selected)))] + (->> selected (map resolve-shape) (filterv (comp not nil?)))))] #(l/derived selected->shapes st/state))) (defn make-hover-shapes-iref diff --git a/frontend/src/app/main/ui/shapes/svg_raw.cljs b/frontend/src/app/main/ui/shapes/svg_raw.cljs index 3eb282a2ac..01442e1617 100644 --- a/frontend/src/app/main/ui/shapes/svg_raw.cljs +++ b/frontend/src/app/main/ui/shapes/svg_raw.cljs @@ -9,14 +9,15 @@ (ns app.main.ui.shapes.svg-raw (:require + [app.common.data :as cd] [app.common.geom.matrix :as gmt] [app.common.geom.point :as gpt] [app.common.geom.shapes :as gsh] + [app.common.uuid :as uuid] [app.main.ui.shapes.attrs :as usa] [app.util.data :as ud] - [app.common.data :as cd] - [app.common.uuid :as uuid] [app.util.object :as obj] + [app.util.svg :as usvg] [cuerdas.core :as str] [rumext.alpha :as mf])) @@ -49,6 +50,8 @@ (defn set-styles [attrs shape] (let [custom-attrs (usa/extract-style-attrs shape) + attrs (cond-> attrs + (string? (:style attrs)) usvg/clean-attrs) style (obj/merge! (clj->js (:style attrs {})) (obj/get custom-attrs "style"))] (-> (clj->js attrs) diff --git a/frontend/src/app/util/svg.cljs b/frontend/src/app/util/svg.cljs new file mode 100644 index 0000000000..eedb6cb443 --- /dev/null +++ b/frontend/src/app/util/svg.cljs @@ -0,0 +1,42 @@ +;; 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/. +;; +;; This Source Code Form is "Incompatible With Secondary Licenses", as +;; defined by the Mozilla Public License, v. 2.0. +;; +;; Copyright (c) 2020-2021 UXBOX Labs SL + +(ns app.util.svg + (:require + [cuerdas.core :as str])) + +(defn clean-attrs + "Transforms attributes to their react equivalent" + [attrs] + (letfn [(transform-key [key] + (-> (name key) + (str/replace ":" "-") + (str/camel) + (keyword))) + + (format-styles [style-str] + (->> (str/split style-str ";") + (map str/trim) + (map #(str/split % ":")) + (group-by first) + (map (fn [[key val]] + (vector + (transform-key key) + (second (first val))))) + (into {}))) + + (map-fn [[key val]] + (cond + (= key :class) [:className val] + (and (= key :style) (string? val)) [key (format-styles val)] + :else (vector (transform-key key) val)))] + + (->> attrs + (map map-fn) + (into {}))))