mirror of https://github.com/logseq/logseq
feat: collape/expand
parent
c0b3e398f6
commit
afb93eca99
|
@ -174,10 +174,10 @@
|
||||||
[parent-state parent-id]
|
[parent-state parent-id]
|
||||||
[:div#mobile-editor-toolbar.bg-base-2.fix-ios-fixed-bottom
|
[:div#mobile-editor-toolbar.bg-base-2.fix-ios-fixed-bottom
|
||||||
[:button.bottom-action
|
[:button.bottom-action
|
||||||
{:on-click #(editor-handler/indent-outdent parent-state true)}
|
{:on-click #(editor-handler/indent-outdent true)}
|
||||||
svg/indent-block]
|
svg/indent-block]
|
||||||
[:button.bottom-action
|
[:button.bottom-action
|
||||||
{:on-click #(editor-handler/indent-outdent parent-state false)}
|
{:on-click #(editor-handler/indent-outdent false)}
|
||||||
svg/outdent-block]
|
svg/outdent-block]
|
||||||
[:button.bottom-action
|
[:button.bottom-action
|
||||||
{:on-click (editor-handler/move-up-down true)}
|
{:on-click (editor-handler/move-up-down true)}
|
||||||
|
|
|
@ -837,12 +837,14 @@
|
||||||
blocks)))
|
blocks)))
|
||||||
|
|
||||||
(defn has-children?
|
(defn has-children?
|
||||||
[repo block-id]
|
([block-id]
|
||||||
(let [db (conn/get-conn repo)]
|
(has-children? (state/get-current-repo) block-id))
|
||||||
(when-let [block (db-utils/entity [:block/uuid block-id])]
|
([repo block-id]
|
||||||
;; perf: early stop
|
(let [db (conn/get-conn repo)]
|
||||||
(let [result (d/datoms db :avet :block/parent (:db/id block))]
|
(when-let [block (db-utils/entity [:block/uuid block-id])]
|
||||||
(boolean (seq result))))))
|
;; perf: early stop
|
||||||
|
(let [result (d/datoms db :avet :block/parent (:db/id block))]
|
||||||
|
(boolean (seq result)))))))
|
||||||
|
|
||||||
;; TODO: improve perf
|
;; TODO: improve perf
|
||||||
(defn with-children-refs
|
(defn with-children-refs
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
(ns frontend.handler.editor
|
(ns frontend.handler.editor
|
||||||
(:require [frontend.state :as state]
|
(:require [frontend.state :as state]
|
||||||
|
[clojure.walk :as w]
|
||||||
[lambdaisland.glogi :as log]
|
[lambdaisland.glogi :as log]
|
||||||
[frontend.db.model :as db-model]
|
[frontend.db.model :as db-model]
|
||||||
[frontend.db.utils :as db-utils]
|
[frontend.db.utils :as db-utils]
|
||||||
|
@ -950,7 +951,8 @@
|
||||||
[(conj r [id (assoc block :level (inc parent-level))])
|
[(conj r [id (assoc block :level (inc parent-level))])
|
||||||
(assoc-in state [(:db/id block) :level] (inc parent-level))]
|
(assoc-in state [(:db/id block) :level] (inc parent-level))]
|
||||||
[(conj r [id block])
|
[(conj r [id block])
|
||||||
state])) [{} level-blocks-map] level-blocks-map)]
|
state]))
|
||||||
|
[{} level-blocks-map] level-blocks-map)]
|
||||||
level-blocks-map))
|
level-blocks-map))
|
||||||
|
|
||||||
(defn- blocks-vec->tree
|
(defn- blocks-vec->tree
|
||||||
|
@ -1673,61 +1675,39 @@
|
||||||
:data [block]}]
|
:data [block]}]
|
||||||
(db/refresh! repo opts)))))))
|
(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
|
;; selections
|
||||||
(defn on-tab
|
(defn on-tab
|
||||||
"direction = :left|:right, only indent or outdent when blocks are siblings"
|
"direction = :left|:right, only indent or outdent when blocks are siblings"
|
||||||
[direction]
|
[direction]
|
||||||
(fn [e]
|
(when-let [repo (state/get-current-repo)]
|
||||||
(when-let [repo (state/get-current-repo)]
|
(let [blocks-dom-nodes (state/get-selection-blocks)
|
||||||
(let [blocks-dom-nodes (state/get-selection-blocks)
|
blocks (seq blocks-dom-nodes)]
|
||||||
blocks (seq blocks-dom-nodes)]
|
(cond
|
||||||
(cond
|
(seq blocks)
|
||||||
(seq blocks)
|
(do
|
||||||
(do
|
(let [lookup-refs (->> (map (fn [block] (when-let [id (dom/attr block "blockid")]
|
||||||
(let [lookup-refs (->> (map (fn [block] (when-let [id (dom/attr block "blockid")]
|
[:block/uuid (medley/uuid id)])) blocks)
|
||||||
[:block/uuid (medley/uuid id)])) blocks)
|
(remove nil?))
|
||||||
(remove nil?))
|
blocks (db/pull-many repo '[*] lookup-refs)
|
||||||
blocks (db/pull-many repo '[*] lookup-refs)
|
blocks (reorder-blocks blocks)
|
||||||
blocks (reorder-blocks blocks)
|
end-node (get-top-level-end-node blocks)
|
||||||
end-node (get-top-level-end-node blocks)
|
end-node-parent (tree/-get-parent end-node)
|
||||||
end-node-parent (tree/-get-parent end-node)
|
top-level-nodes (->> (filter #(= (get-in end-node-parent [:data :db/id])
|
||||||
top-level-nodes (->> (filter #(= (get-in end-node-parent [:data :db/id])
|
(get-in % [:block/parent :db/id])) blocks)
|
||||||
(get-in % [:block/parent :db/id])) blocks)
|
(map outliner-core/block))]
|
||||||
(map outliner-core/block))]
|
(outliner-core/indent-outdent-nodes top-level-nodes (= direction :right))
|
||||||
(outliner-core/indent-outdent-nodes top-level-nodes (= direction :right))
|
(let [opts {:key :block/change
|
||||||
(let [opts {:key :block/change
|
:data blocks}]
|
||||||
:data blocks}]
|
(db/refresh! repo opts)
|
||||||
(db/refresh! repo opts)
|
(let [blocks (doall
|
||||||
(let [blocks (doall
|
(map
|
||||||
(map
|
(fn [block]
|
||||||
(fn [block]
|
(when-let [id (gobj/get block "id")]
|
||||||
(when-let [id (gobj/get block "id")]
|
(when-let [block (gdom/getElement id)]
|
||||||
(when-let [block (gdom/getElement id)]
|
(dom/add-class! block "selected noselect")
|
||||||
(dom/add-class! block "selected noselect")
|
block)))
|
||||||
block)))
|
blocks-dom-nodes))]
|
||||||
blocks-dom-nodes))]
|
(state/set-selection-blocks! blocks)))))))))
|
||||||
(state/set-selection-blocks! blocks)))))
|
|
||||||
|
|
||||||
(gdom/getElement "date-time-picker")
|
|
||||||
nil
|
|
||||||
|
|
||||||
:else
|
|
||||||
(cycle-collapse! e))))))
|
|
||||||
|
|
||||||
(defn- get-link
|
(defn- get-link
|
||||||
[format link label]
|
[format link label]
|
||||||
|
@ -2365,9 +2345,9 @@
|
||||||
(delete-and-update input (dec current-pos) current-pos)))))
|
(delete-and-update input (dec current-pos) current-pos)))))
|
||||||
|
|
||||||
(defn indent-outdent
|
(defn indent-outdent
|
||||||
[state indent?]
|
[indent?]
|
||||||
(state/set-editor-op! :indent-outdent)
|
(state/set-editor-op! :indent-outdent)
|
||||||
(let [{:keys [block block-parent-id value config]} (get-state)]
|
(let [{:keys [block]} (get-state)]
|
||||||
(when block
|
(when block
|
||||||
(let [current-node (outliner-core/block block)]
|
(let [current-node (outliner-core/block block)]
|
||||||
(outliner-core/indent-outdent-nodes [current-node] indent?)
|
(outliner-core/indent-outdent-nodes [current-node] indent?)
|
||||||
|
@ -2378,16 +2358,25 @@
|
||||||
|
|
||||||
(defn keydown-tab-handler
|
(defn keydown-tab-handler
|
||||||
[direction]
|
[direction]
|
||||||
(fn [state e]
|
(fn [e]
|
||||||
(let [input (state/get-input)
|
(cond
|
||||||
pos (:pos (util/get-caret-pos input))]
|
(state/editing?)
|
||||||
(when (and (not (state/get-editor-show-input))
|
(let [input (state/get-input)
|
||||||
(not (state/get-editor-show-date-picker?))
|
pos (:pos (util/get-caret-pos input))]
|
||||||
(not (state/get-editor-show-template-search?)))
|
(when (and (not (state/get-editor-show-input))
|
||||||
(indent-outdent state (not (= :left direction)))
|
(not (state/get-editor-show-date-picker?))
|
||||||
(and input pos
|
(not (state/get-editor-show-template-search?)))
|
||||||
(when-let [input (state/get-input)]
|
(indent-outdent (not (= :left direction)))
|
||||||
(util/move-cursor-to input pos)))))))
|
(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
|
(defn keydown-not-matched-handler
|
||||||
[format]
|
[format]
|
||||||
|
@ -2805,3 +2794,123 @@
|
||||||
(save-current-block! {:force? true})
|
(save-current-block! {:force? true})
|
||||||
(util/forward-kill-word input)
|
(util/forward-kill-word input)
|
||||||
(state/set-edit-content! (state/get-edit-input-id) (.-value input))))
|
(state/set-edit-content! (state/get-edit-input-id) (.-value input))))
|
||||||
|
|
||||||
|
(defn tree-seq-with-level
|
||||||
|
[branch? children root]
|
||||||
|
(let [walk (fn walk [level node]
|
||||||
|
(lazy-seq
|
||||||
|
(cons (assoc node :block/level level)
|
||||||
|
(when (branch? node)
|
||||||
|
(mapcat (partial walk (inc level)) (children node))))))]
|
||||||
|
(walk 1 root)))
|
||||||
|
|
||||||
|
(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)]
|
||||||
|
(cond->>
|
||||||
|
(-> page
|
||||||
|
(db/get-page-blocks-no-cache)
|
||||||
|
(tree/blocks->vec-tree page))
|
||||||
|
|
||||||
|
collapse?
|
||||||
|
(w/postwalk
|
||||||
|
(fn [x]
|
||||||
|
(if (and (map? x) (-> x :block/properties :collapsed))
|
||||||
|
(assoc x :block/children [])
|
||||||
|
x)))
|
||||||
|
|
||||||
|
:default
|
||||||
|
(mapcat (fn [x] (tree-seq-with-level map? :block/children x)))
|
||||||
|
|
||||||
|
:default
|
||||||
|
(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)))))))))
|
||||||
|
|
|
@ -59,14 +59,6 @@
|
||||||
{:desc "Delete / Delete forwards"
|
{:desc "Delete / Delete forwards"
|
||||||
:binding "delete"
|
:binding "delete"
|
||||||
:fn editor-handler/editor-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
|
:editor/new-block
|
||||||
{:desc "Create new block"
|
{:desc "Create new block"
|
||||||
:binding "enter"
|
:binding "enter"
|
||||||
|
@ -79,14 +71,6 @@
|
||||||
{:desc "Rotate the TODO state of the current item"
|
{:desc "Rotate the TODO state of the current item"
|
||||||
:binding "mod+enter"
|
:binding "mod+enter"
|
||||||
:fn editor-handler/cycle-todo!}
|
: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
|
:editor/follow-link
|
||||||
{:desc "Follow link under cursor"
|
{:desc "Follow link under cursor"
|
||||||
:binding "mod+o"
|
:binding "mod+o"
|
||||||
|
@ -199,6 +183,22 @@
|
||||||
{:desc "Delete selected blocks"
|
{:desc "Delete selected blocks"
|
||||||
:binding ["backspace" "delete"]
|
:binding ["backspace" "delete"]
|
||||||
:fn editor-handler/delete-selection}
|
: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
|
:editor/copy
|
||||||
{:desc "Copy"
|
{:desc "Copy"
|
||||||
:binding "mod+c"
|
:binding "mod+c"
|
||||||
|
@ -297,14 +297,6 @@
|
||||||
:binding "t w"
|
:binding "t w"
|
||||||
:fn ui-handler/toggle-wide-mode!}
|
:fn ui-handler/toggle-wide-mode!}
|
||||||
;; :ui/toggle-between-page-and-file route-handler/toggle-between-page-and-file!
|
;; :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
|
:git/commit
|
||||||
{:desc "Git commit message"
|
{:desc "Git commit message"
|
||||||
:binding "g c"
|
:binding "g c"
|
||||||
|
@ -319,8 +311,8 @@
|
||||||
:editor/new-line
|
:editor/new-line
|
||||||
:editor/indent
|
:editor/indent
|
||||||
:editor/outdent
|
:editor/outdent
|
||||||
:ui/fold
|
:editor/collapse-block-children
|
||||||
:ui/un-fold
|
:editor/expand-block-children
|
||||||
:go/search
|
:go/search
|
||||||
:go/search-in-page
|
:go/search-in-page
|
||||||
:editor/undo
|
:editor/undo
|
||||||
|
|
Loading…
Reference in New Issue