فهرست منبع

Merge branch 'master' into feat/integration-plugins-core

charlie 4 سال پیش
والد
کامیت
c99caeffe5

+ 4 - 3
src/main/frontend/components/block.cljs

@@ -1274,9 +1274,7 @@
                                heading-level)
                           (and (get properties :heading)
                                (<= level 6)
-                               level
-                               ;; FIXME: construct the proper level later
-                               2))
+                               level))
         elem (if heading-level
                (keyword (str "h" heading-level
                              (when block-ref? ".inline")))
@@ -1793,6 +1791,9 @@
        :repo repo
        :haschild (str has-child?)}
 
+       level
+       (assoc :level level)
+
        (not slide?)
        (merge attrs)
 

+ 2 - 2
src/main/frontend/components/editor.cljs

@@ -174,10 +174,10 @@
   [parent-state parent-id]
   [:div#mobile-editor-toolbar.bg-base-2.fix-ios-fixed-bottom
    [:button.bottom-action
-    {:on-click #(editor-handler/indent-outdent parent-state true)}
+    {:on-click #(editor-handler/indent-outdent true)}
     svg/indent-block]
    [:button.bottom-action
-    {:on-click #(editor-handler/indent-outdent parent-state false)}
+    {:on-click #(editor-handler/indent-outdent false)}
     svg/outdent-block]
    [:button.bottom-action
     {:on-click (editor-handler/move-up-down true)}

+ 8 - 6
src/main/frontend/db/model.cljs

@@ -837,12 +837,14 @@
       blocks)))
 
 (defn has-children?
-  [repo block-id]
-  (let [db (conn/get-conn repo)]
-    (when-let [block (db-utils/entity [:block/uuid block-id])]
-      ;; perf: early stop
-      (let [result (d/datoms db :avet :block/parent (:db/id block))]
-        (boolean (seq result))))))
+  ([block-id]
+   (has-children? (state/get-current-repo) block-id))
+  ([repo block-id]
+   (let [db (conn/get-conn repo)]
+     (when-let [block (db-utils/entity [:block/uuid block-id])]
+       ;; perf: early stop
+       (let [result (d/datoms db :avet :block/parent (:db/id block))]
+         (boolean (seq result)))))))
 
 ;; TODO: improve perf
 (defn with-children-refs

+ 12 - 5
src/main/frontend/handler/block.cljs

@@ -28,12 +28,21 @@
            block)]
     @ids))
 
+(defn get-block-refs-with-children
+  [block]
+  (->>
+   (tree-seq :block/refs
+             :block/children
+             block)
+   (mapcat :block/refs)
+   (distinct)))
+
 (defn filter-blocks
   [repo ref-blocks filters group-by-page?]
   (let [ref-pages (->> (if group-by-page?
                          (mapcat last ref-blocks)
                          ref-blocks)
-                       (mapcat (fn [b] (concat (:block/refs b) (:block/children-refs b))))
+                       (mapcat (fn [b] (get-block-refs-with-children b)))
                        (concat (when group-by-page? (map first ref-blocks)))
                        (distinct)
                        (map :db/id)
@@ -51,16 +60,14 @@
                        (cond->> ref-blocks
                          (seq exclude-ids)
                          (remove (fn [block]
-                                   (let [ids (set (concat (map :db/id (:block/refs block))
-                                                          (map :db/id (:block/children-refs block))
+                                   (let [ids (set (concat (map :db/id (get-block-refs-with-children block))
                                                           [(:db/id (:block/page block))]))]
                                      (seq (set/intersection exclude-ids ids)))))
 
                          (seq include-ids)
                          (remove (fn [block]
                                    (let [page-block-id (:db/id (:block/page block))
-                                         ids (set (concat (map :db/id (:block/refs block))
-                                                          (map :db/id (:block/children-refs block))))]
+                                         ids (set (map :db/id (get-block-refs-with-children block)))]
                                      (if (and (contains? include-ids page-block-id)
                                               (= 1 (count include-ids)))
                                        (not= page-block-id (first include-ids))

+ 162 - 64
src/main/frontend/handler/editor.cljs

@@ -1,5 +1,6 @@
 (ns frontend.handler.editor
   (:require [frontend.state :as state]
+            [clojure.walk :as w]
             [lambdaisland.glogi :as log]
             [frontend.db.model :as db-model]
             [frontend.db.utils :as db-utils]
@@ -953,7 +954,8 @@
                     [(conj r [id (assoc block :level (inc parent-level))])
                      (assoc-in state [(:db/id block) :level] (inc parent-level))]
                     [(conj r [id block])
-                     state])) [{} level-blocks-map] level-blocks-map)]
+                     state]))
+                [{} level-blocks-map] level-blocks-map)]
     level-blocks-map))
 
 (defn- blocks-vec->tree
@@ -1676,61 +1678,39 @@
                       :data [block]}]
             (db/refresh! repo opts)))))))
 
-(defn expand!
-  []
-  (when-let [current-block (state/get-edit-block)]
-    (remove-block-property! (:block/uuid current-block) :collapsed)))
-
-(defn collapse!
-  []
-  (when-let [current-block (state/get-edit-block)]
-    (set-block-property! (:block/uuid current-block) :collapsed true)))
-
-;; TODO:
-(defn cycle-collapse!
-  [e]
-  )
-
 ;; selections
 (defn on-tab
   "direction = :left|:right, only indent or outdent when blocks are siblings"
   [direction]
-  (fn [e]
-    (when-let [repo (state/get-current-repo)]
-      (let [blocks-dom-nodes (state/get-selection-blocks)
-            blocks (seq blocks-dom-nodes)]
-        (cond
-          (seq blocks)
-          (do
-            (let [lookup-refs (->> (map (fn [block] (when-let [id (dom/attr block "blockid")]
-                                                     [:block/uuid (medley/uuid id)])) blocks)
-                                   (remove nil?))
-                  blocks (db/pull-many repo '[*] lookup-refs)
-                  blocks (reorder-blocks blocks)
-                  end-node (get-top-level-end-node blocks)
-                  end-node-parent (tree/-get-parent end-node)
-                  top-level-nodes (->> (filter #(= (get-in end-node-parent [:data :db/id])
-                                                   (get-in % [:block/parent :db/id])) blocks)
-                                       (map outliner-core/block))]
-              (outliner-core/indent-outdent-nodes top-level-nodes (= direction :right))
-              (let [opts {:key :block/change
-                          :data blocks}]
-                (db/refresh! repo opts)
-                (let [blocks (doall
-                              (map
-                               (fn [block]
-                                 (when-let [id (gobj/get block "id")]
-                                   (when-let [block (gdom/getElement id)]
-                                     (dom/add-class! block "selected noselect")
-                                     block)))
-                               blocks-dom-nodes))]
-                  (state/set-selection-blocks! blocks)))))
-
-          (gdom/getElement "date-time-picker")
-          nil
-
-          :else
-          (cycle-collapse! e))))))
+  (when-let [repo (state/get-current-repo)]
+    (let [blocks-dom-nodes (state/get-selection-blocks)
+          blocks (seq blocks-dom-nodes)]
+      (cond
+        (seq blocks)
+        (do
+          (let [lookup-refs (->> (map (fn [block] (when-let [id (dom/attr block "blockid")]
+                                                    [:block/uuid (medley/uuid id)])) blocks)
+                                 (remove nil?))
+                blocks (db/pull-many repo '[*] lookup-refs)
+                blocks (reorder-blocks blocks)
+                end-node (get-top-level-end-node blocks)
+                end-node-parent (tree/-get-parent end-node)
+                top-level-nodes (->> (filter #(= (get-in end-node-parent [:data :db/id])
+                                                 (get-in % [:block/parent :db/id])) blocks)
+                                     (map outliner-core/block))]
+            (outliner-core/indent-outdent-nodes top-level-nodes (= direction :right))
+            (let [opts {:key :block/change
+                        :data blocks}]
+              (db/refresh! repo opts)
+              (let [blocks (doall
+                            (map
+                             (fn [block]
+                               (when-let [id (gobj/get block "id")]
+                                 (when-let [block (gdom/getElement id)]
+                                   (dom/add-class! block "selected noselect")
+                                   block)))
+                             blocks-dom-nodes))]
+                (state/set-selection-blocks! blocks)))))))))
 
 (defn- get-link
   [format link label]
@@ -2368,9 +2348,9 @@
         (delete-and-update input (dec current-pos) current-pos)))))
 
 (defn indent-outdent
-  [state indent?]
+  [indent?]
   (state/set-editor-op! :indent-outdent)
-  (let [{:keys [block block-parent-id value config]} (get-state)]
+  (let [{:keys [block]} (get-state)]
     (when block
       (let [current-node (outliner-core/block block)]
         (outliner-core/indent-outdent-nodes [current-node] indent?)
@@ -2381,16 +2361,26 @@
 
 (defn keydown-tab-handler
   [direction]
-  (fn [state e]
-    (let [input (state/get-input)
-          pos (:pos (util/get-caret-pos input))]
-      (when (and (not (state/get-editor-show-input))
-                 (not (state/get-editor-show-date-picker?))
-                 (not (state/get-editor-show-template-search?)))
-        (indent-outdent state (not (= :left direction)))
-        (and input pos
-             (when-let [input (state/get-input)]
-               (util/move-cursor-to input pos)))))))
+  (fn [e]
+    (cond
+      (state/editing?)
+      (let [input (state/get-input)
+            pos (:pos (util/get-caret-pos input))]
+        (when (and (not (state/get-editor-show-input))
+                   (not (state/get-editor-show-date-picker?))
+                   (not (state/get-editor-show-template-search?)))
+          (util/stop e)
+          (indent-outdent (not (= :left direction)))
+          (and input pos
+               (when-let [input (state/get-input)]
+                 (util/move-cursor-to input pos)))))
+
+      (state/selection?)
+      (do
+        (util/stop e)
+        (on-tab direction))
+
+      :else nil)))
 
 (defn keydown-not-matched-handler
   [format]
@@ -2808,3 +2798,111 @@
     (save-current-block! {:force? true})
     (util/forward-kill-word input)
     (state/set-edit-content! (state/get-edit-input-id) (.-value input))))
+
+(defn all-blocks-with-level
+  "Return all blocks associated with correct level
+   if :collapse? true, return without any collapsed children
+   for example:
+   - a
+    - b (collapsed)
+     - c
+     - d
+    - e
+   return:
+    blocks
+    [{:block a :level 1}
+     {:block b :level 2}
+     {:block e :level 2}]"
+  [{:keys [collapse?] :or {collapse? false}}]
+  (let [page (state/get-current-page)]
+    (->>
+     (-> page
+         (db/get-page-blocks-no-cache)
+         (tree/blocks->vec-tree page))
+
+     (#(if collapse?
+         (w/postwalk
+          (fn [x]
+            (if (and (map? x) (-> x :block/properties :collapsed))
+              (assoc x :block/children []) x)) %) %))
+
+     (mapcat (fn [x] (tree-seq map? :block/children x)))
+
+     (map (fn [x] (dissoc x :block/children))))))
+
+(defn collapse-block! [block-id]
+  (when (db-model/has-children? block-id)
+    (set-block-property! block-id :collapsed true)))
+
+(defn expand-block! [block-id]
+  (remove-block-property! block-id :collapsed))
+
+(defn expand!
+  []
+  (cond
+    (state/editing?)
+    (when-let [block-id (:block/uuid (state/get-edit-block))]
+      (expand-block! block-id))
+
+    (state/selection?)
+    (do
+      (->> (get-selected-blocks-with-children)
+           (map (fn [dom]
+                  (-> (dom/attr dom "blockid")
+                      medley/uuid
+                      expand-block!)))
+           doall)
+      (clear-selection! nil))
+
+    :else
+    ;; expand one level
+    (let [blocks-with-level (all-blocks-with-level {})
+          max-level (apply max (map :block/level blocks-with-level))]
+      (loop [level 1]
+        (if (> level max-level)
+          nil
+          (let [blocks-to-expand (->> blocks-with-level
+                                 (filter (fn [b] (= (:block/level b) level)))
+                                 (filter (fn [{:block/keys [properties]}]
+                                           (contains? properties :collapsed))))]
+            (if (empty? blocks-to-expand)
+              (recur (inc level))
+              (doseq [{:block/keys [uuid]} blocks-to-expand]
+                (expand-block! uuid)))))))))
+
+
+(defn collapse!
+  []
+  (cond
+    (state/editing?)
+    (when-let [block-id (:block/uuid (state/get-edit-block))]
+      (collapse-block! block-id))
+
+    (state/selection?)
+    (do
+      (->> (get-selected-blocks-with-children)
+           (map (fn [dom]
+                  (-> (dom/attr dom "blockid")
+                      medley/uuid
+                      collapse-block!)))
+           doall)
+      (clear-selection! nil))
+
+    :else
+    ;; collapse by one level from outside
+    (let [blocks-with-level
+          (all-blocks-with-level {:collapse? true})
+          max-level (apply max (map :block/level blocks-with-level))]
+      (loop [level (dec max-level)]
+        (if (zero? level)
+          nil
+          (let [blocks-to-collapse
+                (->> blocks-with-level
+                     (filter (fn [b] (= (:block/level b) level)))
+                     (remove (fn [b]
+                               (or (not (db-model/has-children? (:block/uuid b)))
+                                   (-> b :block/properties :collapsed)))))]
+            (if (empty? blocks-to-collapse)
+              (recur (dec level))
+              (doseq [{:block/keys [uuid]} blocks-to-collapse]
+                (collapse-block! uuid)))))))))

+ 22 - 33
src/main/frontend/modules/outliner/core.cljs

@@ -423,19 +423,8 @@
            result)
          result)))))
 
-(defn- get-node-parents
-  [node limit]
-  (loop [node node
-         limit limit
-         result []]
-    (if (zero? limit)
-      result
-      (if-let [parent (tree/-get-parent node)]
-        (recur parent (dec limit) (conj result (tree/-get-id parent)))
-        result))))
-
 (defn delete-nodes
-  "Delete nodes from the tree, start-node and end-node must be siblings.
+  "Delete nodes from the tree.
   Args:
     start-node: the node at the top of the outliner document.
     end-node: the node at the bottom of the outliner document
@@ -445,27 +434,27 @@
   [start-node end-node block-ids]
   {:pre [(tree/satisfied-inode? start-node)
          (tree/satisfied-inode? end-node)]}
-  (let [sibling? (= (tree/-get-parent-id start-node)
-                    (tree/-get-parent-id end-node))]
-    (when sibling?
-      (ds/auto-transact!
-        [txs-state (ds/new-outliner-txs-state)]
-        {:outliner-op :delete-nodes}
-        (if (= start-node end-node)
-          (delete-node start-node true)
-          (let [right-node (tree/-get-right end-node)
-                end-node-left-nodes (get-left-nodes end-node (count block-ids))
-                start-node-parents-with-self (conj (get-node-parents start-node 1000) (tree/-get-id start-node))]
-            (when (tree/satisfied-inode? right-node)
-              (let [cross-node-id (first (set/intersection (set end-node-left-nodes) (set start-node-parents-with-self)))
-                    cross-node (get-block-by-id cross-node-id)
-                    new-left-id (if (= cross-node start-node)
-                                  (tree/-get-left-id cross-node)
-                                  cross-node-id)
-                    new-right-node (tree/-set-left-id right-node new-left-id)]
-                (tree/-save new-right-node txs-state)))
-            (let [txs (db-outliner/del-blocks block-ids)]
-              (ds/add-txs txs-state txs))))))))
+  (ds/auto-transact!
+   [txs-state (ds/new-outliner-txs-state)]
+   {:outliner-op :delete-nodes}
+   (if (= start-node end-node)
+     (delete-node start-node true)
+     (let [sibling? (= (tree/-get-parent-id start-node)
+                       (tree/-get-parent-id end-node))
+           right-node (tree/-get-right end-node)]
+       (when (tree/satisfied-inode? right-node)
+         (let [left-node-id (if sibling?
+                              (tree/-get-id (tree/-get-left start-node))
+                              (let [end-node-left-nodes (get-left-nodes end-node (count block-ids))
+                                    parents (db/get-block-parents
+                                             (state/get-current-repo)
+                                             (tree/-get-id start-node)
+                                             1000)]
+                                (first (set/intersection (set end-node-left-nodes) parents))))
+               new-right-node (tree/-set-left-id right-node left-node-id)]
+           (tree/-save new-right-node txs-state)))
+       (let [txs (db-outliner/del-blocks block-ids)]
+         (ds/add-txs txs-state txs))))))
 
 (defn first-child?
   [node]

+ 10 - 7
src/main/frontend/modules/outliner/tree.cljs

@@ -38,13 +38,16 @@
   (assoc block :block/children children))
 
 (defn- blocks->vec-tree-aux
-  [blocks root]
-  (let [root {:db/id (:db/id root)}]
-    (some->>
-     (get-children blocks root)
-     (map (fn [block]
-            (let [children (blocks->vec-tree-aux blocks block)]
-              (with-children block children)))))))
+  ([blocks root]
+   (blocks->vec-tree-aux blocks root 1))
+  ([blocks root level]
+   (let [root {:db/id (:db/id root)}]
+     (some->>
+      (get-children blocks root)
+      (map (fn [block]
+             (let [block (assoc block :block/level level)
+                   children (blocks->vec-tree-aux blocks block (inc level))]
+               (with-children block children))))))))
 
 (defn- get-root-and-page
   [root-id]

+ 18 - 26
src/main/frontend/modules/shortcut/config.cljs

@@ -59,14 +59,6 @@
     {:desc    "Delete / Delete forwards"
      :binding "delete"
      :fn      editor-handler/editor-delete}
-    :editor/indent
-    {:desc    "Indent block"
-     :binding "tab"
-     :fn      (editor-handler/keydown-tab-handler :right)}
-    :editor/outdent
-    {:desc    "Outdent block"
-     :binding "shift+tab"
-     :fn      (editor-handler/keydown-tab-handler :left)}
     :editor/new-block
     {:desc    "Create new block"
      :binding "enter"
@@ -79,14 +71,6 @@
     {:desc    "Rotate the TODO state of the current item"
      :binding "mod+enter"
      :fn      editor-handler/cycle-todo!}
-    :editor/expand-block-children
-    {:desc    "Expand"
-     :binding "mod+down"
-     :fn      editor-handler/expand!}
-    :editor/collapse-block-children
-    {:desc    "Collapse"
-     :binding "mod+up"
-     :fn      editor-handler/collapse!}
     :editor/follow-link
     {:desc    "Follow link under cursor"
      :binding "mod+o"
@@ -199,6 +183,22 @@
     {:desc    "Delete selected blocks"
      :binding ["backspace" "delete"]
      :fn      editor-handler/delete-selection}
+    :editor/expand-block-children
+    {:desc    "Expand"
+     :binding "mod+down"
+     :fn      editor-handler/expand!}
+    :editor/collapse-block-children
+    {:desc    "Collapse"
+     :binding "mod+up"
+     :fn      editor-handler/collapse!}
+    :editor/indent
+    {:desc    "Indent block"
+     :binding "tab"
+     :fn      (editor-handler/keydown-tab-handler :right)}
+    :editor/outdent
+    {:desc    "Outdent block"
+     :binding "shift+tab"
+     :fn      (editor-handler/keydown-tab-handler :left)}
     :editor/copy
     {:desc    "Copy"
      :binding "mod+c"
@@ -297,14 +297,6 @@
      :binding "t w"
      :fn      ui-handler/toggle-wide-mode!}
     ;; :ui/toggle-between-page-and-file route-handler/toggle-between-page-and-file!
-    :ui/fold
-    {:desc    "Fold blocks (when not in edit mode)"
-     :binding "tab"
-     :fn      (editor-handler/on-tab :right)}
-    :ui/un-fold
-    {:desc    "Unfold blocks (when not in edit mode)"
-     :binding "shift+tab"
-     :fn      (editor-handler/on-tab :left)}
     :git/commit
     {:desc    "Git commit message"
      :binding "g c"
@@ -319,8 +311,8 @@
     :editor/new-line
     :editor/indent
     :editor/outdent
-    :ui/fold
-    :ui/un-fold
+    :editor/collapse-block-children
+    :editor/expand-block-children
     :go/search
     :go/search-in-page
     :editor/undo