Forráskód Böngészése

Add a command to switch graphs (#4009)

* First pass at select-graph with select component

* Add empty placeholder and move graph switch logic to events

* Remove unused force? in shortcuts

* Document additional option and make disabled open-select consistent

* Address review feedback

Add data check to :graph/switch, translation improvements and update
browser to display graph names same as in the sidebar

Co-authored-by: Tienson Qin <[email protected]>
Gabriel Horner 3 éve
szülő
commit
ec513a54d2

+ 2 - 1
src/main/frontend/components/command_palette.css

@@ -1,4 +1,4 @@
-.cp__palette {
+.cp__palette, .cp__select {
   --palettle-input-height: 64px;
   --palettle-container-height: 75vh;
 
@@ -27,6 +27,7 @@
     }
 
     .command-results-wrap,
+    .item-results-wrap,
     .search-results-wrap > div:first-child {
       overflow-x: hidden;
       overflow-y: auto;

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

@@ -52,10 +52,10 @@
               [:div.has-help
                command-name
                (ui/tippy
-                 {:html doc
-                  :interactive true
-                  :fixed-position? true
-                  :position "right"}
+                {:html doc
+                 :interactive true
+                 :fixed-position? true
+                 :position "right"}
 
                 [:small (svg/help-circle)])]
 
@@ -148,7 +148,7 @@
                               :tippy-distance  24
                               :tippy-position  (if sidebar? "left" "right")}
                              page-name)])
-            :empty-div   [:div.text-gray-500.text-sm.px-4.py-2 "Search for a page"]
+            :empty-placeholder [:div.text-gray-500.text-sm.px-4.py-2 "Search for a page"]
             :class       "black"}))))))
 
 (rum/defcs block-search-auto-complete < rum/reactive
@@ -170,7 +170,7 @@
        result
        {:on-chosen   chosen-handler
         :on-enter    non-exist-block-handler
-        :empty-div   [:div.text-gray-500.pl-4.pr-4 "Search for a block"]
+        :empty-placeholder   [:div.text-gray-500.pl-4.pr-4 "Search for a block"]
         :item-render (fn [{:block/keys [page uuid]}]  ;; content returned from search engine is normalized
                        (let [page (or (:block/original-name page)
                                       (:block/name page))
@@ -222,7 +222,7 @@
            matched-templates
            {:on-chosen   (editor-handler/template-on-chosen-handler id)
             :on-enter    non-exist-handler
-            :empty-div   [:div.text-gray-500.px-4.py-2.text-sm "Search for a template"]
+            :empty-placeholder [:div.text-gray-500.px-4.py-2.text-sm "Search for a template"]
             :item-render (fn [[template _block-db-id]]
                            template)
             :class       "black"}))))))
@@ -441,23 +441,23 @@
               [:input.form-input.block.w-full.pl-2.sm:text-sm.sm:leading-5
                (merge
                 (cond->
-                    {:key           (str "modal-input-" (name id))
-                     :id            (str "modal-input-" (name id))
-                     :type          (or type "text")
-                     :on-change     (fn [e]
-                                      (swap! input-value assoc id (util/evalue e)))
-                     :auto-complete (if (util/chrome?) "chrome-off" "off")}
-                    placeholder
-                    (assoc :placeholder placeholder)
-                    autoFocus
-                    (assoc :auto-focus true))
+                  {:key           (str "modal-input-" (name id))
+                   :id            (str "modal-input-" (name id))
+                   :type          (or type "text")
+                   :on-change     (fn [e]
+                                    (swap! input-value assoc id (util/evalue e)))
+                   :auto-complete (if (util/chrome?) "chrome-off" "off")}
+                  placeholder
+                  (assoc :placeholder placeholder)
+                  autoFocus
+                  (assoc :auto-focus true))
                 (dissoc input-item :id))]])
            (ui/button
-            "Submit"
-            :on-click
-            (fn [e]
-              (util/stop e)
-              (on-submit command @input-value pos)))])))))
+             "Submit"
+             :on-click
+             (fn [e]
+               (util/stop e)
+               (on-submit command @input-value pos)))])))))
 
 (rum/defc absolute-modal < rum/static
   [cp set-default-width? {:keys [top left rect]}]
@@ -501,7 +501,7 @@
               {:top        (+ top offset-top (if (int? y-diff) y-diff 0))
                :max-height to-max-height
                :max-width 700
-                ;; TODO: auto responsive fixed size
+               ;; TODO: auto responsive fixed size
                :width "fit-content"
                :z-index    11}
               (when set-default-width?

+ 3 - 29
src/main/frontend/components/repo.cljs

@@ -12,45 +12,19 @@
             [frontend.handler.export :as export-handler]
             [frontend.handler.page :as page-handler]
             [frontend.handler.repo :as repo-handler]
-            [frontend.handler.route :as route-handler]
             [frontend.handler.ui :as ui-handler]
             [frontend.handler.web.nfs :as nfs-handler]
-            [frontend.handler.notification :as notification]
             [frontend.modules.shortcut.core :as shortcut]
             [frontend.state :as state]
             [frontend.ui :as ui]
             [frontend.util :as util]
-            [frontend.fs :as fs]
             [frontend.version :as version]
             [reitit.frontend.easy :as rfe]
-            [frontend.modules.outliner.file :as outliner-file]
             [rum.core :as rum]
             [frontend.mobile.util :as mobile-util]
             [frontend.text :as text]
             [promesa.core :as p]
-            [electron.ipc :as ipc]
-            [frontend.extensions.srs :as srs]))
-
-;; TODO: move to events
-(defn- open-repo-url [url]
-  (repo-handler/push-if-auto-enabled! (state/get-current-repo))
-  (state/set-current-repo! url)
-  ;; load config
-  (common-handler/reset-config! url nil)
-  (shortcut/refresh!)
-  (when-not (= :draw (state/get-current-route))
-    (route-handler/redirect-to-home!))
-  (when-let [dir-name (config/get-repo-dir url)]
-    (fs/watch-dir! dir-name))
-  (srs/update-cards-due-count!))
-
-(defn- switch-repo-if-writes-finished?
-  [url]
-  (if (outliner-file/writes-finished?)
-    (open-repo-url url)
-    (notification/show!
-     "Please wait seconds until all changes are saved for the current graph."
-     :warning)))
+            [electron.ipc :as ipc]))
 
 (rum/defc add-repo
   [args]
@@ -95,7 +69,7 @@
                  (let [local-dir (config/get-local-dir url)
                        graph-name (text/get-graph-name-from-path local-dir)]
                    [:a {:title local-dir
-                        :on-click #(switch-repo-if-writes-finished? url)}
+                        :on-click #(state/pub-event! [:graph/switch url])}
                     graph-name])
                  [:a {:target "_blank"
                       :href url}
@@ -239,7 +213,7 @@
                               {:title short-repo-name
                                :hover-detail repo-path ;; show full path on hover
                                :options {:class "ml-1"
-                                         :on-click #(switch-repo-if-writes-finished? url)}}))
+                                         :on-click #(state/pub-event! [:graph/switch url])}}))
                           switch-repos)
               links (->>
                      (concat repo-links

+ 105 - 0
src/main/frontend/components/select.cljs

@@ -0,0 +1,105 @@
+(ns frontend.components.select
+  "Generic component for fuzzy searching items to select an item. See
+  select-config to add a new use or select-type for this component. To use the
+  new select-type, set :ui/open-select to the select-type. See
+  :select-graph/open command for an example."
+  (:require [frontend.modules.shortcut.core :as shortcut]
+            [frontend.context.i18n :as i18n]
+            [frontend.search :as search]
+            [frontend.state :as state]
+            [frontend.ui :as ui]
+            [frontend.util :as util]
+            [frontend.db :as db]
+            [frontend.text :as text]
+            [rum.core :as rum]
+            [frontend.config :as config]
+            [reitit.frontend.easy :as rfe]))
+
+(rum/defc render-item
+  [{:keys [id value]} chosen?]
+  [:div.inline-grid.grid-cols-4.gap-x-4.w-full
+   {:class (when chosen? "chosen")}
+   [:span.col-span-3 value]
+   [:div.col-span-1.justify-end.tip.flex
+    (when id
+      [:code.opacity-20.bg-transparent id])]])
+
+(rum/defcs select <
+  (shortcut/disable-all-shortcuts)
+  (rum/local "" ::input)
+  {:will-unmount (fn [state]
+                   (state/set-state! [:ui/open-select] nil)
+                   state)}
+  [state {:keys [items limit on-chosen empty-placeholder prompt-key]
+          :or {limit 100
+               prompt-key :select/default-prompt}}]
+  (rum/with-context [[t] i18n/*tongue-context*]
+    (let [input (::input state)]
+      [:div.cp__select.cp__select-main
+       [:div.input-wrap
+        [:input.cp__select-input.w-full
+         {:type        "text"
+          :placeholder (t prompt-key)
+          :auto-focus  true
+          :value       @input
+          :on-change   (fn [e] (reset! input (util/evalue e)))}]]
+
+       [:div.item-results-wrap
+        (ui/auto-complete
+         (search/fuzzy-search items @input :limit limit :extract-fn :value)
+         {:item-render render-item
+          :class       "cp__select-results"
+          :on-chosen   (fn [x]
+                         (state/close-modal!)
+                         (on-chosen x))
+          :empty-placeholder (empty-placeholder t)})]])))
+
+(defn select-config
+  "Config that supports multiple types (uses) of this component. To add a new
+  type, add a key with the value being a map with the following keys:
+
+  * :items-fn - fn that returns items with a :value key that are used for the
+    fuzzy search and selection. Items can have an optional :id and are displayed
+    lightly for a given item.
+  * :on-chosen - fn that is given item when it is chosen.
+  * :empty-placeholder - fn that returns hiccup html to render if no matched graphs found.
+  * :prompt-key - dictionary keyword that prompts when components is first open.
+    Defaults to :select/default-prompt."
+  []
+  {:select-graph
+   {:items-fn (fn []
+                (->>
+                 (state/get-repos)
+                 (remove (fn [{:keys [url]}]
+                           (or (config/demo-graph? url)
+                               (= url (state/get-current-repo)))))
+                 (map (fn [{:keys [url]}]
+                        {:value (text/get-graph-name-from-path
+                                 ;; TODO: Use helper when a common one is refactored
+                                 ;; from components.repo
+                                 (if (config/local-db? url)
+                                   (config/get-local-dir url)
+                                   (db/get-repo-path url)))
+                         :id (config/get-repo-dir url)
+                         :graph url}))))
+    :prompt-key :select.graph/prompt
+    :on-chosen #(state/pub-event! [:graph/switch (:graph %)])
+    :empty-placeholder (fn [t]
+                         [:div.px-4.py-2
+                          [:div.mb-2 (t :select.graph/empty-placeholder-description)]
+                          (ui/button
+                           (t :select.graph/add-graph)
+                           :href (rfe/href :repo-add)
+                           :on-click state/close-modal!)])}})
+
+(rum/defc select-modal < rum/reactive
+  []
+  (when-let [select-type (state/sub [:ui/open-select])]
+    (let [select-type-config (get (select-config) select-type)]
+      (state/set-modal!
+       #(select (-> select-type-config
+                    (select-keys [:on-chosen :empty-placeholder :prompt-key])
+                    (assoc :items ((:items-fn select-type-config)))))
+       {:fullscreen? false
+        :close-btn?  false}))
+    nil))

+ 2 - 0
src/main/frontend/components/sidebar.cljs

@@ -10,6 +10,7 @@
             [frontend.components.theme :as theme]
             [frontend.components.widgets :as widgets]
             [frontend.components.plugins :as plugins]
+            [frontend.components.select :as select]
             [frontend.config :as config]
             [frontend.context.i18n :as i18n]
             [frontend.db :as db]
@@ -545,6 +546,7 @@
         (ui/modal)
         (ui/sub-modal)
         (command-palette/command-palette-modal)
+        (select/select-modal)
         (custom-context-menu)
         (plugins/custom-js-installer {:t t
                                       :current-repo current-repo

+ 1 - 1
src/main/frontend/components/widgets.cljs

@@ -172,7 +172,7 @@
 
                        :local
                        [(rum/with-key (android-permission-alert)
-                          "andoird-permission-alert")
+                          "android-permission-alert")
                         (rum/with-key (add-local-directory)
                           "add-local-directory")]
 

+ 4 - 2
src/main/frontend/config.cljs

@@ -307,8 +307,10 @@
 (defonce local-repo "local")
 
 (defn demo-graph?
-  []
-  (= (state/get-current-repo) local-repo))
+  ([]
+   (demo-graph? (state/get-current-repo)))
+  ([graph]
+   (= graph local-repo)))
 
 (defonce local-assets-dir "assets")
 (defonce recycle-dir ".recycle")

+ 11 - 2
src/main/frontend/dicts.cljs

@@ -390,7 +390,11 @@
 
         :tips/all-done "All Done"
 
-        :command-palette/prompt "Type a command"}
+        :command-palette/prompt "Type a command"
+        :select/default-prompt "Select one"
+        :select.graph/prompt "Select a graph"
+        :select.graph/empty-placeholder-description "No matched graphs. Do you want to add another one?"
+        :select.graph/add-graph "Yes, add another graph"}
 
    :de {:help/about "Über Logseq"
         :on-boarding/demo-graph "This is a demo graph, changes will not be saved until you open a local folder."
@@ -1958,7 +1962,12 @@
         :user/delete-your-account "Eliminar su cuenta"
         :user/delete-account-notice "Todas sus páginas publicadas en Logseq serán eliminadas."
 
-        :help/shortcut-page-title "Atajos personalizados"}
+        :help/shortcut-page-title "Atajos personalizados"
+
+        :select/prompt "Seleccione uno"
+        :select.graph/prompt "Seleccione un grafo"
+        :select.graph/empty-placeholder-description "No encontramos un grafo. Queries añadir otro?"
+        :select.graph/add-graph "Si, añadame otro grafo"}
 
    :nb-NO {:on-boarding/title "Hei, og velkommen til Logseq!"
            :on-boarding/sharing "deling"

+ 24 - 1
src/main/frontend/handler/events.cljs

@@ -21,7 +21,10 @@
             [frontend.handler.notification :as notification]
             [frontend.handler.page :as page-handler]
             [frontend.handler.ui :as ui-handler]
+            [frontend.handler.repo :as repo-handler]
+            [frontend.handler.route :as route-handler]
             [frontend.modules.shortcut.core :as st]
+            [frontend.modules.outliner.file :as outliner-file]
             [frontend.commands :as commands]
             [frontend.spec :as spec]
             [frontend.state :as state]
@@ -32,7 +35,8 @@
             [frontend.modules.instrumentation.posthog :as posthog]
             [frontend.mobile.util :as mobile-util]
             [frontend.encrypt :as encrypt]
-            [promesa.core :as p]))
+            [promesa.core :as p]
+            [frontend.fs :as fs]))
 
 ;; TODO: should we move all events here?
 
@@ -77,6 +81,25 @@
     (db/set-key-value repo :ast/version db-schema/ast-version)
     (srs/update-cards-due-count!)))
 
+(defn- graph-switch [graph]
+  (repo-handler/push-if-auto-enabled! (state/get-current-repo))
+  (state/set-current-repo! graph)
+  ;; load config
+  (common-handler/reset-config! graph nil)
+  (st/refresh!)
+  (when-not (= :draw (state/get-current-route))
+    (route-handler/redirect-to-home!))
+  (when-let [dir-name (config/get-repo-dir graph)]
+    (fs/watch-dir! dir-name))
+  (srs/update-cards-due-count!))
+
+(defmethod handle :graph/switch [[_ graph]]
+  (if (outliner-file/writes-finished?)
+    (graph-switch graph)
+    (notification/show!
+     "Please wait seconds until all changes are saved for the current graph."
+     :warning)))
+
 (defmethod handle :graph/migrated [[_ _repo]]
   (js/alert "Graph migrated."))
 

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

@@ -182,13 +182,11 @@
 
    :editor/up                      {:desc    "Move cursor up / Select up"
                                     :binding "up"
-                                    :fn      (editor-handler/shortcut-up-down :up)
-                                    :force?  true}
+                                    :fn      (editor-handler/shortcut-up-down :up)}
 
    :editor/down                    {:desc    "Move cursor down / Select down"
                                     :binding "down"
-                                    :fn      (editor-handler/shortcut-up-down :down)
-                                    :force?  true}
+                                    :fn      (editor-handler/shortcut-up-down :down)}
 
    :editor/left                    {:desc    "Move cursor left / Open selected block at beginning"
                                     :binding "left"
@@ -225,13 +223,11 @@
 
    :editor/expand-block-children   {:desc    "Expand"
                                     :binding "mod+down"
-                                    :fn      editor-handler/expand!
-                                    :force?  true}
+                                    :fn      editor-handler/expand!}
 
    :editor/collapse-block-children {:desc    "Collapse"
                                     :binding "mod+up"
-                                    :fn      editor-handler/collapse!
-                                    :force?  true}
+                                    :fn      editor-handler/collapse!}
 
    :editor/indent                  {:desc    "Indent block"
                                     :binding "tab"
@@ -316,8 +312,11 @@
 
    :command-palette/toggle         {:desc    "Toggle command palette"
                                     :binding "mod+shift+p"
-                                    :fn      (fn [] (state/toggle! :ui/command-palette-open?))
-                                    :force?   true}
+                                    :fn      (fn [] (state/toggle! :ui/command-palette-open?))}
+
+   :select-graph/open              {:desc    "Open select graph component"
+                                    :fn      (fn [] (state/set-state! :ui/open-select :select-graph))
+                                    :binding "mod+shift+g"}
 
    :command/run                    (when (util/electron?)
                                      {:desc    "Run git command"
@@ -432,7 +431,7 @@
    :ui/toggle-cards                 {:desc    "Toggle cards"
                                      :binding "t c"
                                      :fn      ui-handler/toggle-cards!}
-  ;; :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!
 
    :git/commit                      {:desc    "Git commit message"
                                      :binding "c"
@@ -499,6 +498,7 @@
     :shortcut.handler/editor-global
     (->
      (build-category-map [:command-palette/toggle
+                          :select-graph/open
                           :editor/cycle-todo
                           :editor/up
                           :editor/down
@@ -680,6 +680,7 @@
     :pdf/next-page
     :command/run
     :command-palette/toggle
+    :select-graph/open
     :sidebar/clear
     :sidebar/open-today-page
     :search/re-index

+ 2 - 1
src/main/frontend/state.cljs

@@ -1216,7 +1216,8 @@
            :modal/label ""
            :modal/show? false
            :modal/fullscreen? false
-           :modal/panel-content nil)))
+           :modal/panel-content nil
+           :ui/open-select nil)))
 
 (defn get-db-batch-txs-chan
   []

+ 3 - 3
src/main/frontend/ui.cljs

@@ -420,7 +420,7 @@
    {:keys [on-chosen
            on-shift-chosen
            get-group-name
-           empty-div
+           empty-placeholder
            item-render
            class]}]
   (let [current-idx (get state ::current-idx)]
@@ -452,8 +452,8 @@
                  item-cp)
 
                item-cp))])]
-       (when empty-div
-         empty-div))]))
+       (when empty-placeholder
+         empty-placeholder))]))
 
 (def datepicker frontend.ui.date-picker/date-picker)