Browse Source

feat: show preview on focus

Peng Xiao 4 years ago
parent
commit
93dd55d0ba

+ 45 - 28
src/main/frontend/components/block.cljs

@@ -371,38 +371,55 @@
        :else
        (get page-entity :block/original-name page-name)))])
 
+(defn- use-delayed-open [open? page-name timeout]
+  "A react hook to debounce open? value.
+  If open? changed from false to open, there will be a `timeout` delay.
+  Otherwise, the value will be changed to false immediately"
+  (let [[deval set-deval!] (rum/use-state false)]
+    (rum/use-effect!
+      (fn []
+        (if open? (let [timer (js/setTimeout #(set-deval! open?) timeout)]
+                    #(js/clearTimeout timer))
+                  (set-deval! open?))) ;; immediately change
+      [open? page-name])
+    deval))
+
 (rum/defc page-preview-trigger
-  [{:keys [children sidebar? tippy-position fixed-position?] :as config} page-name]
+  [{:keys [children sidebar? tippy-position tippy-distance fixed-position? open?] :as config} page-name]
   (let [page-entity (db/entity [:block/name page-name])
         redirect-page-name (model/get-redirect-page-name page-name (:block/alias? config))
-        page-original-name (model/get-page-original-name redirect-page-name)]
-    (ui/tippy {:html        (fn []
-                              [:div.tippy-wrapper.overflow-y-auto.p-4
-                               {:style {:width          600
-                                        :text-align     "left"
-                                        :font-weight    500
-                                        :max-height     600
-                                        :padding-bottom 64}}
-                               (if (and (string? page-original-name) (string/includes? page-original-name "/"))
-                                 [:div.my-2
-                                  (->>
-                                    (for [page (string/split page-original-name #"/")]
-                                      (when (and (string? page) page)
-                                        (page-reference false page {} nil)))
-                                    (interpose [:span.mx-2.opacity-30 "/"]))]
-                                 [:h2.font-bold.text-lg (if (= page-name redirect-page-name)
-                                                          page-original-name
-                                                          [:span
-                                                           [:span.text-sm.mr-2 "Alias:"]
-                                                           page-original-name])])
-                               (let [page (db/entity [:block/name (string/lower-case redirect-page-name)])]
-                                 (editor-handler/insert-first-page-block-if-not-exists! redirect-page-name)
-                                 (when-let [f (state/get-page-blocks-cp)]
-                                   (f (state/get-current-repo) page {:sidebar? sidebar? :preview? true})))])
-               :interactive true
-               :delay       [1000, 100]
+        page-original-name (model/get-page-original-name redirect-page-name)
+        debounced-open? (use-delayed-open open? page-name 1000)
+        html-template (fn []
+                        [:div.tippy-wrapper.overflow-y-auto.p-4
+                         {:style {:width          600
+                                  :text-align     "left"
+                                  :font-weight    500
+                                  :max-height     600
+                                  :padding-bottom 64}}
+                         (if (and (string? page-original-name) (string/includes? page-original-name "/"))
+                           [:div.my-2
+                            (->>
+                              (for [page (string/split page-original-name #"/")]
+                                (when (and (string? page) page)
+                                  (page-reference false page {} nil)))
+                              (interpose [:span.mx-2.opacity-30 "/"]))]
+                           [:h2.font-bold.text-lg (if (= page-name redirect-page-name)
+                                                    page-original-name
+                                                    [:span
+                                                     [:span.text-sm.mr-2 "Alias:"]
+                                                     page-original-name])])
+                         (let [page (db/entity [:block/name (string/lower-case redirect-page-name)])]
+                           (editor-handler/insert-first-page-block-if-not-exists! redirect-page-name)
+                           (when-let [f (state/get-page-blocks-cp)]
+                             (f (state/get-current-repo) page {:sidebar? sidebar? :preview? true})))])]
+    (ui/tippy {:html            html-template
+               :interactive     true
+               :open?           debounced-open?
+               :delay           [1000, 100]
                :fixed-position? fixed-position?
-               :position    (or tippy-position "top")}
+               :position        (or tippy-position "top")
+               :distance        (or tippy-distance 10)}
               children)))
 
 (rum/defc page-cp

+ 17 - 14
src/main/frontend/components/editor.cljs

@@ -75,6 +75,9 @@
                                                      {:last-pattern commands/angle-bracket}))
         :class     "black"}))))
 
+(defn- in-sidebar? [el]
+  (not (.contains (.getElementById js/document "left-container") el)))
+
 (rum/defc page-search < rum/reactive
   {:will-unmount (fn [state] (reset! editor-handler/*selected-text nil) state)}
   [id format]
@@ -85,6 +88,7 @@
         (let [current-pos (cursor/pos input)
               edit-content (or (state/sub [:editor/content id]) "")
               edit-block (state/sub :editor/block)
+              sidebar? (in-sidebar? input)
               q (or
                  @editor-handler/*selected-text
                  (when (state/sub :editor/show-page-search-hashtag?)
@@ -94,20 +98,19 @@
               matched-pages (when-not (string/blank? q)
                               (editor-handler/get-matched-pages q))]
           (ui/auto-complete
-           matched-pages
-           {:on-chosen   (page-handler/on-chosen-handler input id q pos format)
-            :on-enter    #(page-handler/page-not-exists-handler input id q current-pos)
-            :item-render (fn [item chosen?]
-                           [:div.py-2.flex.page-search-menu-item
-                            [[:div (search/highlight-exact-query item q)]
-                             [:div.flex-1]
-                             ;; Ideally, we may want to trigger preview on focused
-                             (block/page-preview-trigger
-                               {:children [:div.page-search-menu-item-preview "Preview"]
-                                :tippy-position "right"}
-                               item)]])
-            :empty-div   [:div.text-gray-500.pl-4.pr-4 "Search for a page"]
-            :class       "black"}))))))
+            matched-pages
+            {:on-chosen   (page-handler/on-chosen-handler input id q pos format)
+             :on-enter    #(page-handler/page-not-exists-handler input id q current-pos)
+             :item-render (fn [page-name chosen?]
+                            [:div.py-2 (block/page-preview-trigger
+                                         {:children        [:div (search/highlight-exact-query page-name q)]
+                                          :open?           chosen?
+                                          :fixed-position? true
+                                          :tippy-distance  24
+                                          :tippy-position  (if sidebar? "left" "right")}
+                                         page-name)])
+             :empty-div   [:div.text-gray-500.pl-4.pr-4 "Search for a page"]
+             :class       "black"}))))))
 
 (rum/defcs block-search-auto-complete < rum/reactive
   {:init (fn [state]

+ 0 - 14
src/main/frontend/components/editor.css

@@ -63,17 +63,3 @@ pre {
   background: #f6f8fa;
   background: var(--ls-secondary-background-color);
 }
-
-
-.page-search-menu-item-preview {
-  opacity: 0;
-}
-
-.page-search-menu-item:hover {
-  .page-search-menu-item-preview {
-    opacity: 0.5;
-    &:hover {
-      opacity: 1;
-    }
-  }
-}

+ 6 - 4
src/main/frontend/components/page.cljs

@@ -62,16 +62,18 @@
 (defn- open-first-block!
   [state]
   (let [blocks (nth (:rum/args state) 1)
-        block (first blocks)]
+        block (first blocks)
+        preview? (nth (:rum/args state) 4)]
     (when (and (= (count blocks) 1)
-               (string/blank? (:block/content block)))
+               (string/blank? (:block/content block))
+               (not preview?))
       (editor-handler/edit-block! block :max (:block/format block) (:block/uuid block))))
   state)
 
 (rum/defc page-blocks-inner <
   {:did-mount open-first-block!
    :did-update open-first-block!}
-  [page-name page-blocks hiccup sidebar?]
+  [page-name page-blocks hiccup sidebar? preview?]
   [:div.page-blocks-inner
    (rum/with-key
      (content/content page-name
@@ -129,7 +131,7 @@
                              config)
               hiccup-config (common-handler/config-with-document-mode hiccup-config)
               hiccup (block/->hiccup page-blocks hiccup-config {})]
-          (page-blocks-inner page-name page-blocks hiccup sidebar?))))))
+          (page-blocks-inner page-name page-blocks hiccup sidebar? preview?))))))
 
 (defn contents-page
   [page]

+ 10 - 7
src/main/frontend/ui.cljs

@@ -5,6 +5,7 @@
             ["react-textarea-autosize" :as TextareaAutosize]
             ["react-resize-context" :as Resize]
             ["react-tippy" :as react-tippy]
+            ["@popperjs/core" :as popperjs]
             [frontend.util :as util]
             [frontend.mixins :as mixins]
             [frontend.handler.notification :as notification-handler]
@@ -628,26 +629,28 @@
 
 (rum/defcs tippy < rum/static
   (rum/local false ::mounted?)
-  [state {:keys [fixed-position?] :as opts} child]
+  [state {:keys [fixed-position? open?] :as opts} child]
   (let [*mounted? (::mounted? state)
-        mounted? @*mounted?]
+        mounted? @*mounted?
+        manual (not= open? nil)]
     (Tippy (->
             (merge {:arrow true
                     :sticky true
                     :theme "customized"
                     :disabled (not (state/enable-tooltip?))
                     :unmountHTMLWhenHide true
-                    :open @*mounted?
+                    :open (if manual open? @*mounted?)
+                    :trigger (if manual "manual" "mouseenter focus")
                     ;; See https://github.com/tvkhoa/react-tippy/issues/13
                     :popperOptions (if fixed-position?
-                                      {:modifiers {:preventOverflow {:enabled false}
-                                                   :flip {:enabled false}
-                                                   :hide {:enabled false}}}
+                                      {:modifiers {:flip {:enabled false}
+                                                   :hide {:enabled false}
+                                                   :preventOverflow {:enabled false}}}
                                       {})
                     :onShow #(reset! *mounted? true)
                     :onHide #(reset! *mounted? false)}
                    opts)
-            (assoc :html (if mounted?
+            (assoc :html (if (or open? mounted?)
                            (when-let [html (:html opts)]
                              (if (fn? html)
                                (html)