diff --git a/common/src/app/common/geom/shapes/transforms.cljc b/common/src/app/common/geom/shapes/transforms.cljc index 3d98526807..c3035df279 100644 --- a/common/src/app/common/geom/shapes/transforms.cljc +++ b/common/src/app/common/geom/shapes/transforms.cljc @@ -43,14 +43,16 @@ (mapv #(gpt/add % move-vec)))) (defn move-position-data - [position-data dx dy] + ([position-data {:keys [x y]}] + (move-position-data position-data x y)) - (when (some? position-data) - (cond->> position-data - (d/num? dx dy) - (mapv #(-> % - (update :x + dx) - (update :y + dy)))))) + ([position-data dx dy] + (when (some? position-data) + (cond->> position-data + (d/num? dx dy) + (mapv #(-> % + (update :x + dx) + (update :y + dy))))))) (defn move "Move the shape relatively to its current diff --git a/frontend/resources/styles/main/partials/sidebar-document-history.scss b/frontend/resources/styles/main/partials/sidebar-document-history.scss index f7fc042886..02d786450b 100644 --- a/frontend/resources/styles/main/partials/sidebar-document-history.scss +++ b/frontend/resources/styles/main/partials/sidebar-document-history.scss @@ -4,6 +4,16 @@ // // Copyright (c) KALEIDOS INC +.history-debug-overlay { + background: $color-gray-50; + bottom: 0; + max-height: 500px; + overflow-y: auto; + position: absolute; + width: 500px; + z-index: 1000; +} + .history-toolbox { display: flex; flex-direction: column; diff --git a/frontend/src/app/main/data/workspace.cljs b/frontend/src/app/main/data/workspace.cljs index 6ce0e0e0be..2dca29d370 100644 --- a/frontend/src/app/main/data/workspace.cljs +++ b/frontend/src/app/main/data/workspace.cljs @@ -1969,3 +1969,6 @@ (dm/export dwv/update-viewport-size) (dm/export dwv/start-panning) (dm/export dwv/finish-panning) + +;; Undo +(dm/export dwu/reinitialize-undo) diff --git a/frontend/src/app/main/data/workspace/modifiers.cljs b/frontend/src/app/main/data/workspace/modifiers.cljs index 7402917884..4df2a1353c 100644 --- a/frontend/src/app/main/data/workspace/modifiers.cljs +++ b/frontend/src/app/main/data/workspace/modifiers.cljs @@ -114,6 +114,18 @@ (reduce set-child ignore-tree children)))) +(defn assoc-position-data + [shape position-data old-shape] + (let [deltav (gpt/to-vec (gpt/point (:selrect old-shape)) + (gpt/point (:selrect shape))) + position-data + (-> position-data + (gsh/move-position-data deltav))] + (cond-> shape + (d/not-empty? position-data) + (assoc :position-data position-data)))) + + (defn update-grow-type [shape old-shape] (let [auto-width? (= :auto-width (:grow-type shape)) @@ -319,7 +331,8 @@ (ptk/reify ::apply-modifiers ptk/WatchEvent (watch [_ state _] - (let [objects (wsh/lookup-page-objects state) + (let [text-modifiers (get state :workspace-text-modifier) + objects (wsh/lookup-page-objects state) object-modifiers (if modifiers (calculate-modifiers state modifiers) (get state :workspace-modifiers)) @@ -342,9 +355,13 @@ ids (fn [shape] (let [modif (get-in object-modifiers [(:id shape) :modifiers]) - text-shape? (cph/text-shape? shape)] + text-shape? (cph/text-shape? shape) + position-data (when text-shape? + (dm/get-in text-modifiers [(:id shape) :position-data]))] (-> shape (gsh/transform-shape modif) + (cond-> (d/not-empty? position-data) + (assoc-position-data position-data shape)) (cond-> text-shape? (update-grow-type shape))))) {:reg-objects? true @@ -366,7 +383,11 @@ :grow-type :layout-item-h-sizing :layout-item-v-sizing + :position-data ]}) + ;; We've applied the text-modifier so we can dissoc the temporary data + (fn [state] + (update state :workspace-text-modifier #(apply dissoc % ids))) (clear-local-transform)) (if undo-transation? (rx/of (dwu/commit-undo-transaction undo-id)) diff --git a/frontend/src/app/main/data/workspace/texts.cljs b/frontend/src/app/main/data/workspace/texts.cljs index 3355bfc3f6..da24c5b22b 100644 --- a/frontend/src/app/main/data/workspace/texts.cljs +++ b/frontend/src/app/main/data/workspace/texts.cljs @@ -344,7 +344,7 @@ (when (or (and (not-changed? (:width shape) new-width) (= (:grow-type shape) :auto-width)) (and (not-changed? (:height shape) new-height) (or (= (:grow-type shape) :auto-height) (= (:grow-type shape) :auto-width)))) - (rx/of (dch/update-shapes [id] update-fn {:reg-objects? true :save-undo? false}) + (rx/of (dch/update-shapes [id] update-fn {:reg-objects? true :save-undo? true}) (ptk/data-event :layout/update [id])))))))) @@ -377,7 +377,7 @@ (gpt/point (:selrect shape))) new-shape - (update new-shape :position-data gsh/move-position-data (:x delta-move) (:y delta-move))] + (update new-shape :position-data gsh/move-position-data delta-move)] new-shape)) diff --git a/frontend/src/app/main/ui/workspace.cljs b/frontend/src/app/main/ui/workspace.cljs index 93193089c6..8aba4efbb1 100644 --- a/frontend/src/app/main/ui/workspace.cljs +++ b/frontend/src/app/main/ui/workspace.cljs @@ -27,6 +27,7 @@ [app.main.ui.workspace.libraries] [app.main.ui.workspace.nudge] [app.main.ui.workspace.sidebar :refer [left-sidebar right-sidebar]] + [app.main.ui.workspace.sidebar.history :refer [history-toolbox]] [app.main.ui.workspace.textpalette :refer [textpalette]] [app.main.ui.workspace.viewport :refer [viewport]] [app.util.dom :as dom] @@ -72,6 +73,10 @@ (when (debug? :coordinates) [:& coordinates/coordinates {:colorpalette? colorpalette?}]) + (when (debug? :history-overlay) + [:div.history-debug-overlay + [:button {:on-click #(st/emit! dw/reinitialize-undo)} "CLEAR"] + [:& history-toolbox]]) [:& viewport {:file file :wlocal wlocal :wglobal wglobal diff --git a/frontend/src/app/main/ui/workspace/shapes/text/viewport_texts_html.cljs b/frontend/src/app/main/ui/workspace/shapes/text/viewport_texts_html.cljs index bfd704d7d9..ff6bab60a6 100644 --- a/frontend/src/app/main/ui/workspace/shapes/text/viewport_texts_html.cljs +++ b/frontend/src/app/main/ui/workspace/shapes/text/viewport_texts_html.cljs @@ -140,6 +140,10 @@ (let [text-shapes (obj/get props "text-shapes") prev-text-shapes (hooks/use-previous text-shapes) + ;; We store in the state the texts still pending to be calculated so we can + ;; get its position + pending-update (mf/use-state {}) + text-change? (fn [id] (let [new-shape (get text-shapes id) @@ -153,12 +157,23 @@ changed-texts (mf/use-memo - (mf/deps text-shapes) - #(->> (keys text-shapes) - (filter text-change?) - (map (d/getf text-shapes)))) + (mf/deps text-shapes @pending-update) + #(let [pending-shapes (into #{} (vals @pending-update))] + (->> (keys text-shapes) + (filter (fn [id] + (or (contains? pending-shapes id) + (text-change? id)))) + (map (d/getf text-shapes))))) - handle-update-shape (mf/use-callback update-text-shape)] + handle-update-shape + (mf/use-callback + (fn [shape node] + ;; Unique to indentify the pending state + (let [uid (js/Symbol)] + (swap! pending-update assoc uid (:id shape)) + (p/then + (update-text-shape shape node) + #(swap! pending-update dissoc uid)))))] [:.text-changes-renderer (for [{:keys [id] :as shape} changed-texts] diff --git a/frontend/src/app/main/ui/workspace/sidebar/options/menus/text.cljs b/frontend/src/app/main/ui/workspace/sidebar/options/menus/text.cljs index 8f3d81ed66..8db3e57f60 100644 --- a/frontend/src/app/main/ui/workspace/sidebar/options/menus/text.cljs +++ b/frontend/src/app/main/ui/workspace/sidebar/options/menus/text.cljs @@ -12,6 +12,7 @@ [app.main.data.workspace.changes :as dch] [app.main.data.workspace.libraries :as dwl] [app.main.data.workspace.texts :as dwt] + [app.main.data.workspace.undo :as dwu] [app.main.fonts :as fonts] [app.main.refs :as refs] [app.main.store :as st] @@ -20,7 +21,7 @@ [app.main.ui.workspace.sidebar.options.menus.typography :refer [typography-entry typography-options]] [app.util.dom :as dom] [app.util.i18n :as i18n :refer [tr]] - [app.util.timers :as tm] + [app.util.timers :as ts] [cuerdas.core :as str] [rumext.v2 :as mf])) @@ -164,7 +165,12 @@ (let [grow-type (:grow-type values) handle-change-grow (fn [_ grow-type] - (st/emit! (dch/update-shapes ids #(assoc % :grow-type grow-type))) + (let [uid (js/Symbol)] + (st/emit! + (dwu/start-undo-transaction uid) + (dch/update-shapes ids #(assoc % :grow-type grow-type))) + ;; We asynchronously commit so every sychronous event is resolved first and inside the transaction + (ts/schedule #(st/emit! (dwu/commit-undo-transaction uid)))) (when (some? on-blur) (on-blur)))] [:div.align-icons @@ -304,7 +310,7 @@ :show-recent true :on-blur (fn [] - (tm/schedule + (ts/schedule 100 (fn [] (when (not= "INPUT" (-> (dom/get-active) (dom/get-tag-name))) diff --git a/frontend/src/app/main/ui/workspace/viewport.cljs b/frontend/src/app/main/ui/workspace/viewport.cljs index da5d13c89f..538d27c7ef 100644 --- a/frontend/src/app/main/ui/workspace/viewport.cljs +++ b/frontend/src/app/main/ui/workspace/viewport.cljs @@ -225,7 +225,9 @@ ;; inside a foreign object "dummy" so this awkward behaviour is take into account [:svg {:style {:top 0 :left 0 :position "fixed" :width "100%" :height "100%" :opacity (when-not (debug? :html-text) 0)}} [:foreignObject {:x 0 :y 0 :width "100%" :height "100%"} - [:div {:style {:pointer-events (when-not (debug? :html-text) "none")}} + [:div {:style {:pointer-events (when-not (debug? :html-text) "none") + ;; some opacity because to debug auto-width events will fill the screen + :opacity 0.6}} [:& stvh/viewport-texts {:key (dm/str "texts-" page-id) :page-id page-id