Просмотр исходного кода

enhance(edit): add smooth-select action

kkkiio 3 лет назад
Родитель
Сommit
140fe27ad7

+ 42 - 18
src/main/frontend/handler/editor.cljs

@@ -1218,32 +1218,36 @@
                    blocks)]
       (state/exit-editing-and-set-selected-blocks! blocks direction))))
 
-(defn on-select-block
+(defn- select-block-up-down
   [direction]
-  (fn [_event]
-    (cond
+  (cond
       ;; when editing, quit editing and select current block
-      (state/editing?)
-      (state/exit-editing-and-set-selected-blocks! [(gdom/getElement (state/get-editing-block-dom-id))])
+    (state/editing?)
+(state/exit-editing-and-set-selected-blocks! [(gdom/getElement (state/get-editing-block-dom-id))])
 
       ;; when selection and one block selected, select next block
-      (and (state/selection?) (== 1 (count (state/get-selection-blocks))))
-      (let [f (if (= :up direction) util/get-prev-block-non-collapsed util/get-next-block-non-collapsed-skip)
-            element (f (first (state/get-selection-blocks)))]
-        (when element
-          (state/conj-selection-block! element direction)))
+    (and (state/selection?) (== 1 (count (state/get-selection-blocks))))
+(let [f (if (= :up direction) util/get-prev-block-non-collapsed util/get-next-block-non-collapsed-skip)
+      element (f (first (state/get-selection-blocks)))]
+  (when element
+    (state/conj-selection-block! element direction)))
 
       ;; if same direction, keep conj on same direction
-      (and (state/selection?) (= direction (state/get-selection-direction)))
-      (let [f (if (= :up direction) util/get-prev-block-non-collapsed util/get-next-block-non-collapsed-skip)
-            first-last (if (= :up direction) first last)
-            element (f (first-last (state/get-selection-blocks)))]
-        (when element
-          (state/conj-selection-block! element direction)))
+    (and (state/selection?) (= direction (state/get-selection-direction)))
+(let [f (if (= :up direction) util/get-prev-block-non-collapsed util/get-next-block-non-collapsed-skip)
+      first-last (if (= :up direction) first last)
+      element (f (first-last (state/get-selection-blocks)))]
+  (when element
+    (state/conj-selection-block! element direction)))
 
       ;; if different direction, keep clear until one left
-      (state/selection?)
-      (clear-last-selected-block!))))
+    (state/selection?)
+    (clear-last-selected-block!)))
+
+(defn on-select-block
+  [direction]
+  (fn [_event]
+    (select-block-up-down direction)))
 
 (defn save-block-aux!
   [block value opts]
@@ -3040,6 +3044,26 @@
         :else
         (select-first-last direction)))))
 
+(defn shortcut-select-up-down [direction]
+  (fn [e]
+    (util/stop e)
+    (if (state/editing?)
+      (let [input (state/get-input)
+            selected-start (util/get-selection-start input)
+            selected-end (util/get-selection-end input)
+            [anchor cursor] (case (util/get-selection-direction input)
+                              "backward" [selected-end selected-start]
+                              [selected-start selected-end])
+            cursor-rect (cursor/get-caret-pos input cursor)]
+        (if
+          ;; if the move is to cross block boundary, select the whole block
+         (or (and (= direction :up) (cursor/textarea-cursor-rect-first-row? cursor-rect))
+             (and (= direction :down) (cursor/textarea-cursor-rect-last-row? cursor-rect)))
+          (select-block-up-down direction)
+          ;; simulate text selection
+          (cursor/select-up-down input direction anchor cursor-rect)))
+      (select-block-up-down direction))))
+
 (defn open-selected-block!
   [direction e]
   (when-let [block-id (some-> (state/get-selection-blocks)

+ 11 - 1
src/main/frontend/modules/shortcut/config.cljs

@@ -180,6 +180,12 @@
    :editor/select-block-down       {:binding "shift+down"
                                     :fn      (editor-handler/on-select-block :down)}
 
+   :editor/select-up               {:binding false
+                                    :fn      (editor-handler/shortcut-select-up-down :up)}
+
+   :editor/select-down             {:binding false
+                                    :fn      (editor-handler/shortcut-select-up-down :down)}
+
    :editor/delete-selection        {:binding ["backspace" "delete"]
                                     :fn      editor-handler/delete-selection}
 
@@ -451,6 +457,8 @@
                           :editor/down
                           :editor/left
                           :editor/right
+                          :editor/select-up
+                          :editor/select-down
                           :editor/move-block-up
                           :editor/move-block-down
                           :editor/open-edit
@@ -592,7 +600,9 @@
     :editor/forward-kill-word
     :editor/backward-kill-word
     :editor/replace-block-reference-at-point
-    :editor/paste-text-in-one-block-at-point]
+    :editor/paste-text-in-one-block-at-point
+    :editor/select-up
+    :editor/select-down]
 
    :shortcut.category/block-selection
    [:editor/open-edit

+ 2 - 0
src/main/frontend/modules/shortcut/dicts.cljc

@@ -53,6 +53,8 @@
    :editor/down                    "Move cursor down / Select down"
    :editor/left                    "Move cursor left / Open selected block at beginning"
    :editor/right                   "Move cursor right / Open selected block at end"
+   :editor/select-up               "Select content above"
+   :editor/select-down             "Select content below"
    :editor/move-block-up           "Move block up"
    :editor/move-block-down         "Move block down"
    :editor/open-edit               "Edit selected block"

+ 5 - 0
src/main/frontend/util.cljc

@@ -368,6 +368,11 @@
   (when input
     (.-selectionEnd input)))
 
+(defn get-selection-direction
+  [input]
+  (when input
+    (.-selectionDirection input)))
+
 (defn get-first-or-last-line-pos
   [input]
   (let [pos (get-selection-start input)

+ 41 - 31
src/main/frontend/util/cursor.cljs

@@ -28,23 +28,23 @@
   where offset position is needed.
 
   If you only need character position, use `pos` instead. Do NOT call this."
-  [input]
-  (when input
-    (let [pos (util/get-selection-start input)
-          rect (bean/->clj (.. input (getBoundingClientRect) (toJSON)))]
-      (try
-        (some-> (gdom/getElement "mock-text")
-                gdom/getChildren
-                array-seq
-                (util/nth-safe pos)
-                mock-char-pos
-                (assoc :rect rect))
-        (catch :default _e
-          (js/console.log "index error" _e)
-          {:pos pos
-           :rect rect
-           :left js/Number.MAX_SAFE_INTEGER
-           :top js/Number.MAX_SAFE_INTEGER})))))
+  ([input] (get-caret-pos input (util/get-selection-start input)))
+  ([input pos]
+   (when input
+     (let [rect (bean/->clj (.. input (getBoundingClientRect) (toJSON)))]
+       (try
+         (some-> (gdom/getElement "mock-text")
+                 gdom/getChildren
+                 array-seq
+                 (util/nth-safe pos)
+                 mock-char-pos
+                 (assoc :rect rect))
+         (catch :default _e
+           (js/console.log "index error" _e)
+           {:pos pos
+            :rect rect
+            :left js/Number.MAX_SAFE_INTEGER
+            :top js/Number.MAX_SAFE_INTEGER}))))))
 
 
 (defn pos [input]
@@ -171,42 +171,42 @@
                    inc))]
     (move-cursor-to input idx)))
 
-(defn textarea-cursor-first-row? [input]
+(defn textarea-cursor-rect-first-row? [cursor]
   (let [elms   (-> (gdom/getElement "mock-text")
                    gdom/getChildren
                    array-seq)
-        cursor (-> input
-                   (get-caret-pos))
         tops   (->> elms
                     (map mock-char-pos)
                     (map :top)
                     (distinct))]
     (= (first tops) (:top cursor))))
 
-(defn textarea-cursor-last-row? [input]
+(defn textarea-cursor-first-row? [input]
+  (textarea-cursor-rect-first-row? (get-caret-pos input)))
+
+
+(defn textarea-cursor-rect-last-row? [cursor]
   (let [elms   (-> (gdom/getElement "mock-text")
                    gdom/getChildren
                    array-seq)
-        cursor (-> input
-                   (get-caret-pos))
         tops   (->> elms
                     (map mock-char-pos)
                     (map :top)
                     (distinct))]
     (= (last tops) (:top cursor))))
 
-(defn- move-cursor-up-down
-  [input direction]
+(defn textarea-cursor-last-row? [input]
+  (textarea-cursor-rect-last-row? (get-caret-pos input)))
+
+(defn- next-cursor-pos-up-down [direction cursor]
   (let [elms  (-> (gdom/getElement "mock-text")
                   gdom/getChildren
                   array-seq)
-        cusor (-> input
-                  (get-caret-pos))
         chars (->> elms
                    (map mock-char-pos)
                    (group-by :top))
         tops  (sort (keys chars))
-        tops-p (partition-by #(== (:top cusor) %) tops)
+        tops-p (partition-by #(== (:top cursor) %) tops)
         line-next
         (if (= :up direction)
           (-> tops-p first last)
@@ -214,14 +214,18 @@
         lefts
         (->> (get chars line-next)
              (partition-by (fn [char-pos]
-                             (<= (:left char-pos) (:left cusor)))))
+                             (<= (:left char-pos) (:left cursor)))))
         left-a (-> lefts first last)
         left-c (-> lefts last first)
         closer
         (if (> 2 (count lefts))
           left-a
-          (closer left-a cusor left-c))]
-    (move-cursor-to input (:pos closer))))
+          (closer left-a cursor left-c))]
+    (:pos closer)))
+
+(defn- move-cursor-up-down
+  [input direction]
+    (move-cursor-to input (next-cursor-pos-up-down direction (get-caret-pos input))))
 
 (defn move-cursor-up [input]
   (move-cursor-up-down input :up))
@@ -229,6 +233,12 @@
 (defn move-cursor-down [input]
   (move-cursor-up-down input :down))
 
+(defn select-up-down [input direction anchor cursor-rect]
+  (let [next-cursor (next-cursor-pos-up-down direction cursor-rect)]
+    (if (<= anchor next-cursor)
+      (.setSelectionRange input anchor next-cursor "forward")
+      (.setSelectionRange input next-cursor anchor "backward"))))
+
 (comment
   ;; previous implementation of up/down
   (defn move-cursor-up