1
0
Эх сурвалжийг харах

fix(editor): handle backspace when at beginning of line in embed page/block (#8421)

* fix(editor): handle backspace when pos is 0 on embed page

* fix(editor): embed block should also be handled

* fix(editor): handle backspace when pos is 0 on embed page

* fix(editor): embed block should also be handled

* refactor(editor): reduce repeated code

* Add tests for deleting block with backspace

Also replace get-prev-block-non-collapsed-in-embed which
isn't being used enough to merit another copy and paste

---------

Co-authored-by: Gabriel Horner <[email protected]>
situ2001 2 жил өмнө
parent
commit
235c9fbb58

+ 16 - 12
src/main/frontend/handler/editor.cljs

@@ -807,7 +807,7 @@
    (delete-block! repo true))
   ([repo delete-children?]
    (state/set-editor-op! :delete)
-   (let [{:keys [id block-id block-parent-id value format]} (get-state)]
+   (let [{:keys [id block-id block-parent-id value format config]} (get-state)]
      (when block-id
        (let [page-id (:db/id (:block/page (db/entity [:block/uuid block-id])))
              page-blocks-count (and page-id (db/get-page-blocks-count repo page-id))]
@@ -823,24 +823,28 @@
              (when-not (and has-children? left-has-children?)
                (when block-parent-id
                  (let [block-parent (gdom/getElement block-parent-id)
-                       sibling-block (util/get-prev-block-non-collapsed-non-embed block-parent)
+                       sibling-block (if (:embed? config)
+                                       (util/get-prev-block-non-collapsed
+                                        block-parent
+                                        {:container (util/rec-get-blocks-container block-parent)})
+                                       (util/get-prev-block-non-collapsed-non-embed block-parent))
                        {:keys [prev-block new-content move-fn]} (move-to-prev-block repo sibling-block format id value false)
                        concat-prev-block? (boolean (and prev-block new-content))
                        transact-opts (cond->
-                                       {:outliner-op :delete-blocks}
+                                      {:outliner-op :delete-blocks}
                                        concat-prev-block?
                                        (assoc :concat-data
                                               {:last-edit-block (:block/uuid block)}))]
                    (outliner-tx/transact! transact-opts
-                     (if concat-prev-block?
-                       (let [prev-block' (if (seq (:block/_refs block-e))
-                                           (assoc prev-block
-                                                  :block/uuid (:block/uuid block)
-                                                  :block.temp/additional-properties (:block/properties block))
-                                           prev-block)]
-                         (delete-block-aux! block delete-children?)
-                         (save-block! repo prev-block' new-content {:editor/op :delete}))
-                       (delete-block-aux! block delete-children?)))
+                                          (if concat-prev-block?
+                                            (let [prev-block' (if (seq (:block/_refs block-e))
+                                                                (assoc prev-block
+                                                                       :block/uuid (:block/uuid block)
+                                                                       :block.temp/additional-properties (:block/properties block))
+                                                                prev-block)]
+                                              (delete-block-aux! block delete-children?)
+                                              (save-block! repo prev-block' new-content {:editor/op :delete}))
+                                            (delete-block-aux! block delete-children?)))
                    (move-fn)))))))))
    (state/set-editor-op! nil)))
 

+ 20 - 11
src/main/frontend/util.cljc

@@ -778,9 +778,13 @@
             (rec-get-blocks-content-section (gobj/get node "parentNode"))))))
 
 #?(:cljs
-   (defn get-blocks-noncollapse []
-     (->> (d/sel "div:not(.reveal) .ls-block")
-          (filter (fn [b] (some? (gobj/get b "offsetParent")))))))
+   (defn get-blocks-noncollapse
+     ([]
+      (->> (d/sel "div:not(.reveal) .ls-block")
+           (filter (fn [b] (some? (gobj/get b "offsetParent"))))))
+     ([blocks-container]
+      (->> (d/sel blocks-container "div:not(.reveal) .ls-block")
+           (filter (fn [b] (some? (gobj/get b "offsetParent"))))))))
 
 #?(:cljs
    (defn remove-embedded-blocks [blocks]
@@ -848,14 +852,19 @@
 
 #?(:cljs
    (defn get-prev-block-non-collapsed
-     [block]
-     (when-let [blocks (get-blocks-noncollapse)]
-       (let [block-id (.-id block)
-             block-ids (mapv #(.-id %) blocks)]
-         (when-let [index (.indexOf block-ids block-id)]
-           (let [idx (dec index)]
-             (when (>= idx 0)
-               (nth-safe blocks idx))))))))
+     "Gets previous non-collapsed block. If given a container
+      looks up blocks in that container e.g. for embed"
+     ([block] (get-prev-block-non-collapsed block {}))
+     ([block {:keys [container]}]
+      (when-let [blocks (if container
+                          (get-blocks-noncollapse container)
+                          (get-blocks-noncollapse))]
+        (let [block-id (.-id block)
+              block-ids (mapv #(.-id %) blocks)]
+          (when-let [index (.indexOf block-ids block-id)]
+            (let [idx (dec index)]
+              (when (>= idx 0)
+                (nth-safe blocks idx)))))))))
 
 #?(:cljs
    (defn get-prev-block-non-collapsed-non-embed

+ 69 - 1
src/test/frontend/handler/editor_test.cljs

@@ -6,7 +6,9 @@
             [frontend.test.helper :as test-helper :refer [load-test-files]]
             [frontend.db.model :as model]
             [frontend.state :as state]
-            [frontend.util.cursor :as cursor]))
+            [frontend.util.cursor :as cursor]
+            [goog.dom :as gdom]
+            [frontend.util :as util]))
 
 (use-fixtures :each test-helper/start-and-destroy-db)
 
@@ -255,3 +257,69 @@
 
       (editor/save-block! repo block-uuid "# bar")
       (is (= "# bar" (:block/content (model/query-block-by-uuid block-uuid)))))))
+
+(defn- delete-block
+  [db block {:keys [embed?]}]
+  (let [sibling-block (d/entity db (get-in block [:block/left :db/id]))
+        first-block (d/entity db (get-in sibling-block [:block/left :db/id]))
+        block-dom-id "ls-block-block-to-delete"]
+    (with-redefs [editor/get-state (constantly {:block-id (:block/uuid block)
+                                                :block-parent-id block-dom-id
+                                                :config {:embed? embed?}})
+                ;; stub for delete-block
+                  gdom/getElement (constantly #js {:id block-dom-id})
+                ;; stub since not testing moving
+                  editor/edit-block! (constantly nil)
+                  util/get-blocks-noncollapse (constantly (mapv
+                                                           (fn [m]
+                                                             #js {:id (:id m)
+                                                                  ;; for dom/attr
+                                                                  :getAttribute #({"blockid" (str (:block-uuid m))
+                                                                                   "data-embed" (if embed? "true" "false")} %)})
+                                                           [{:id "ls-block-first-block"
+                                                             :block-uuid (:block/uuid first-block)}
+                                                            {:id "ls-block-sibling-block"
+                                                             :block-uuid (:block/uuid sibling-block)}
+                                                            {:id block-dom-id
+                                                             :block-uuid (:block/uuid block)}]))]
+      (editor/delete-block! test-helper/test-db false))))
+
+(deftest ^:focus delete-block!
+  (testing "backspace deletes empty block"
+    (load-test-files [{:file/path "pages/page1.md"
+                       :file/content "\n
+- b1
+- b2
+-"}])
+    (let [conn (db/get-db test-helper/test-db false)
+          block (->> (d/q '[:find (pull ?b [*])
+                            :where [?b :block/content ""] [?b :block/page [:block/name "page1"]]]
+                          @conn)
+                     ffirst)
+          _ (delete-block @conn block {})
+          updated-blocks (->> (d/q '[:find (pull ?b [*])
+                                     :where [?b :block/content] [(missing? $ ?b :block/pre-block?)]]
+                                   @conn)
+                              (map (comp :block/content first)))]
+      (is (= ["b1" "b2"] updated-blocks) "Block is deleted"))
+    (test-helper/reset-test-db!))
+
+  (testing "backspace deletes empty block in embedded context"
+    ;; testing embed at this layer doesn't require an embed block since
+    ;; delete-block handles all the embed setup
+    (load-test-files [{:file/path "pages/page1.md"
+                       :file/content "\n
+- b1
+- b2
+-"}])
+    (let [conn (db/get-db test-helper/test-db false)
+          block (->> (d/q '[:find (pull ?b [*])
+                            :where [?b :block/content ""] [?b :block/page [:block/name "page1"]]]
+                          @conn)
+                     ffirst)
+          _ (delete-block @conn block {:embed? true})
+          updated-blocks (->> (d/q '[:find (pull ?b [*])
+                                     :where [?b :block/content] [(missing? $ ?b :block/pre-block?)]]
+                                   @conn)
+                              (map (comp :block/content first)))]
+      (is (= ["b1" "b2"] updated-blocks) "Block is deleted"))))

+ 5 - 0
src/test/frontend/test/helper.cljs

@@ -14,6 +14,11 @@
   []
   (conn/destroy-all!))
 
+(defn reset-test-db!
+  []
+  (destroy-test-db!)
+  (start-test-db!))
+
 (defn load-test-files
   "Given a collection of file maps, loads them into the current test-db.
 This can be called in synchronous contexts as no async fns should be invoked"