diff --git a/web/src/main/frontend/components/content.cljs b/web/src/main/frontend/components/content.cljs index be8059af1..e832cfbdd 100644 --- a/web/src/main/frontend/components/content.cljs +++ b/web/src/main/frontend/components/content.cljs @@ -76,9 +76,10 @@ (state/hide-custom-context-menu!) ;; enable scroll - (let [main (d/by-id "main")] + (let [main (d/by-id "main-content")] (d/remove-class! main "overflow-hidden") - (d/add-class! main "overflow-y-scroll")) + (d/add-class! main "overflow-y-auto") + ) (handler/clear-selection!))) @@ -88,9 +89,9 @@ (util/stop e) (let [client-x (gobj/get e "clientX") client-y (gobj/get e "clientY")] - (let [main (d/by-id "main")] + (let [main (d/by-id "main-content")] ;; disable scroll - (d/remove-class! main "overflow-y-scroll") + (d/remove-class! main "overflow-y-auto") (d/add-class! main "overflow-hidden")) (state/show-custom-context-menu!) diff --git a/web/src/main/frontend/components/hiccup.cljs b/web/src/main/frontend/components/hiccup.cljs index 4361c50ee..83813376d 100644 --- a/web/src/main/frontend/components/hiccup.cljs +++ b/web/src/main/frontend/components/hiccup.cljs @@ -176,10 +176,8 @@ ["Target" s] [:a {:id s} s] - ["Radio_Target" s] [:a {:id s} s] - ;; [:a {:href (str "/page/" (util/url-encode s))} (str "<<<" s ">>>")] ["Email" address] (let [{:keys [local_part domain]} address @@ -194,7 +192,13 @@ (when-let [heading (db/get-heading-by-uuid (uuid id))] [:span [:span.text-gray-500 "(("] - [:a {:href (str "/page/" id)} + [:a {:href (str "/page/" id) + :on-click (fn [e] + (util/stop e) + (when (gobj/get e "shiftKey") + (state/sidebar-add-block! :heading-ref {:heading (:heading config) + :ref-heading heading}) + (handler/show-right-sidebar)))} (->elem :span.block-ref (map-inline config (:heading/title heading)))] [:span.text-gray-500 "))"]]))) @@ -212,7 +216,14 @@ ;; page reference [:span.page-reference [:span.text-gray-500 "[["] - [:a {:href (str "/page/" (util/url-encode s))} s] + [:a {:href (str "/page/" (util/url-encode s)) + :on-click (fn [e] + (util/stop e) + (when (gobj/get e "shiftKey") + (state/sidebar-add-block! :page-ref + {:heading (:heading config) + :ref-page s}) + (handler/show-right-sidebar)))} s] [:span.text-gray-500 "]]"]]) :else (let [href (string-of-url url) @@ -222,8 +233,8 @@ (->elem :a (cond-> - {:href href - :target "_blank"} + {:href href + :target "_blank"} title (assoc :title title)) (map-inline config label)))))) @@ -344,10 +355,15 @@ [:span ""])] [:a.flex.flex-row.items-center.justify-center (cond-> - {:style {:width 14 - :height 24}} + {:style {:width 14 + :height 24}} (not dummy?) - (assoc :href (str "/page/" uuid))) + (assoc :href (str "/page/" uuid) + :on-click (fn [e] + (util/stop e) + (when (gobj/get e "shiftKey") + (state/sidebar-add-block! :heading (:heading config)) + (handler/show-right-sidebar))))) [:svg {:height 10 :width 10 :fill "currentColor" @@ -359,7 +375,8 @@ (rum/defcs heading-cp < rum/reactive (rum/local false ::collapsed?) [state {:heading/keys [uuid idx level children meta content dummy? lock? show-page? page format] :as heading} heading-part config] - (let [ref? (boolean (:ref? config)) + (let [config (assoc config :heading heading) + ref? (boolean (:ref? config)) edit-input-id (str "edit-heading-" (if ref? (:id config)) uuid) edit? (state/sub [:editor/editing? edit-input-id]) heading-id (str "ls-heading-parent-" (if ref? (:id config)) uuid) @@ -417,8 +434,7 @@ (swap! state/state assoc :edit-content content :edit-heading heading) - (state/set-edit-input-id! edit-input-id) - ))} + (state/set-edit-input-id! edit-input-id)))} heading-part ;; non-heading children @@ -452,7 +468,8 @@ (defn heading [config {:heading/keys [uuid title tags marker level priority anchor meta numbering children format] :as t}] - (let [agenda? (= (:id config) "agenda") + (let [config (assoc config :heading t) + agenda? (= (:id config) "agenda") checkbox (heading-checkbox t (str "mr-1 cursor")) marker-cp (if (contains? #{"DOING" "IN-PROGRESS" "WAIT"} marker) diff --git a/web/src/main/frontend/components/journal.cljs b/web/src/main/frontend/components/journal.cljs index 70c474b95..5d5d3e279 100644 --- a/web/src/main/frontend/components/journal.cljs +++ b/web/src/main/frontend/components/journal.cljs @@ -3,13 +3,15 @@ [frontend.util :as util] [frontend.handler :as handler] [frontend.db :as db] + [frontend.state :as state] [clojure.string :as string] [frontend.ui :as ui] [frontend.format :as format] [frontend.components.content :as content] [frontend.components.hiccup :as hiccup] [frontend.components.reference :as reference] - [frontend.utf8 :as utf8])) + [frontend.utf8 :as utf8] + [goog.object :as gobj])) (rum/defc journal-cp [[title headings format]] @@ -20,7 +22,12 @@ encoded-page-name (util/url-encode (string/capitalize title))] [:div.flex-1 - [:a.initial-color {:href (str "/page/" encoded-page-name)} + [:a.initial-color {:href (str "/page/" encoded-page-name) + :on-click (fn [e] + (util/stop e) + (when (gobj/get e "shiftKey") + (state/sidebar-add-block! :page {:name title}) + (handler/show-right-sidebar)))} [:h1.title title]] (content/content encoded-page-name diff --git a/web/src/main/frontend/components/page.cljs b/web/src/main/frontend/components/page.cljs index 58edb3070..70e1589b2 100644 --- a/web/src/main/frontend/components/page.cljs +++ b/web/src/main/frontend/components/page.cljs @@ -12,7 +12,8 @@ [frontend.format :as format] [frontend.components.content :as content] [frontend.config :as config] - [frontend.db :as db])) + [frontend.db :as db] + [goog.object :as gobj])) (defn- get-page-name [state] @@ -31,7 +32,7 @@ ;; A page is just a logical heading (rum/defcs page < rum/reactive - [state] + [state option] (let [encoded-page-name (get-page-name state) page-name (string/capitalize (util/url-decode encoded-page-name)) format (db/get-page-format page-name) @@ -46,13 +47,6 @@ hiccup (hiccup/->hiccup page-headings {:id encoded-page-name :start-level start-level}) - ref-headings (if heading-id - (db/get-heading-referenced-headings heading-id) - (db/get-page-referenced-headings page-name)) - ref-headings (mapv (fn [heading] (assoc heading :heading/show-page? true)) ref-headings) - ref-hiccup (hiccup/->hiccup ref-headings {:id encoded-page-name - :start-level start-level}) - page-name (string/capitalize page-name) page-name (if heading? (:page/name (db/entity (:db/id (:heading/page (first page-headings))))) page-name) @@ -60,28 +54,30 @@ starred? (contains? (set (some->> (state/sub [:config repo :starred]) (map string/capitalize))) - page-name)] + page-name) + sidebar? (:sidebar? option)] [:div.flex-1 - [:div.flex.flex-row - [:h1.title - page-name] - [:a.ml-1.text-gray-500.hover:text-gray-700 - {:class (if starred? "text-gray-800") - :on-click (fn [] - (handler/star-page! page-name starred?))} - (if starred? - (svg/star-solid "stroke-current") - (svg/star-outline "stroke-current h-5 w-5"))]] + (when-not sidebar? + [:div.flex.flex-row + [:a {:on-click (fn [e] + (util/stop e) + (when (gobj/get e "shiftKey") + (state/sidebar-add-block! :page {:name page-name}) + (handler/show-right-sidebar)))} + [:h1.title + page-name]] + [:a.ml-1.text-gray-500.hover:text-gray-700 + {:class (if starred? "text-gray-800") + :on-click (fn [] + (handler/star-page! page-name starred?))} + (if starred? + (svg/star-solid "stroke-current") + (svg/star-outline "stroke-current h-5 w-5"))]]) (content/content encoded-page-name {:hiccup hiccup}) - (let [n-ref (count ref-headings)] - (if (> n-ref 0) - [:h2.font-bold.text-gray-400.mt-6 (let [] - (str n-ref " Linked References"))])) - (content/content encoded-page-name - {:hiccup ref-hiccup})])) + (reference/references page-name)])) (rum/defc all-pages < rum/reactive [] diff --git a/web/src/main/frontend/components/reference.cljs b/web/src/main/frontend/components/reference.cljs index c3f22a6a4..b3e60a15e 100644 --- a/web/src/main/frontend/components/reference.cljs +++ b/web/src/main/frontend/components/reference.cljs @@ -14,9 +14,13 @@ (rum/defc references < rum/reactive [page-name] - (let [page-name (string/capitalize page-name) + (let [heading? (util/uuid-string? page-name) + heading-id (and heading? (uuid page-name)) + page-name (string/capitalize page-name) encoded-page-name (util/url-encode page-name) - ref-headings (db/get-page-referenced-headings page-name) + ref-headings (if heading-id + (db/get-heading-referenced-headings heading-id) + (db/get-page-referenced-headings page-name)) ref-headings (mapv (fn [heading] (assoc heading :heading/show-page? true)) ref-headings) ref-hiccup (hiccup/->hiccup ref-headings {:id encoded-page-name :start-level 2 diff --git a/web/src/main/frontend/components/right_sidebar.cljs b/web/src/main/frontend/components/right_sidebar.cljs new file mode 100644 index 000000000..a0eea65f0 --- /dev/null +++ b/web/src/main/frontend/components/right_sidebar.cljs @@ -0,0 +1,48 @@ +(ns frontend.components.right-sidebar + (:require [rum.core :as rum] + [frontend.ui :as ui] + [frontend.components.svg :as svg] + [frontend.components.page :as page] + [frontend.handler :as handler] + [frontend.state :as state] + [medley.core :as medley])) + +(defn sidebar-item + [block-type block-data] + (case block-type + :page-ref + ["Page reference" (str block-data)] + + :heading-ref + ["Block reference" (str block-data)] + + :heading + ["Block " (str block-data)] + + :page + (let [page-name (:name block-data)] + [page-name (page/page {:parameters {:path {:name page-name}} + :sidebar? true})]) + + ["" [:span]])) + +(rum/defc sidebar < rum/reactive + [] + (let [blocks (state/sub :sidebar/blocks)] + [:div#right-sidebar.flex.flex-col.p-2.shadow-xs.overflow-y-auto {:style {:padding-bottom 300}} + (for [[idx [block-type block-data]] (medley/indexed blocks)] + [:div.sidebar-item {:key (str "sidebar-block-" idx)} + (let [[title component] (sidebar-item block-type block-data)] + [:div.flex.flex-col + [:div.flex.flex-row.justify-between + [:div.flex.flex-row.justify-center + [:a.hover:text-gray-900.text-gray-500.flex.items-center + svg/plus] + [:div.ml-2 {:style {:font-size "1.5rem"}} title]] + [:a.close.hover:text-gray-900.text-gray-500.flex.items-center + {:on-click (fn [] + (state/sidebar-remove-block! idx) + (when (empty? (state/get-sidebar-blocks)) + (handler/hide-right-sidebar)))} + svg/close]] + [:div component]])])])) diff --git a/web/src/main/frontend/components/sidebar.cljs b/web/src/main/frontend/components/sidebar.cljs index db845a662..00e6a3365 100644 --- a/web/src/main/frontend/components/sidebar.cljs +++ b/web/src/main/frontend/components/sidebar.cljs @@ -8,6 +8,7 @@ [frontend.components.search :as search] [frontend.components.settings :as settings] [frontend.components.svg :as svg] + [frontend.components.right-sidebar :as right-sidebar] [goog.crypt.base64 :as b64] [frontend.util :as util] [frontend.state :as state] @@ -29,25 +30,25 @@ (if current-repo (let [repos (state/sub [:me :repos])] (if (> (count repos) 1) - (ui/dropdown-with-links - (fn [{:keys [toggle-fn]}] - [:a.hover:text-gray-200.text-gray-500.font-bold {:on-click toggle-fn} - [:span (string/capitalize (util/take-at-most (db/get-repo-name current-repo) 20))] - [:span.dropdown-caret.ml-1 {:style {:border-top-color "#6b7280"}}]]) - (mapv - (fn [{:keys [id url]}] - {:title (db/get-repo-name url) - :options {:on-click (fn [] - (state/set-current-repo! url))}}) - (remove (fn [repo] - (= current-repo (:url repo))) - repos)) - (util/hiccup->class - "origin-top-right.absolute.left-0.mt-2.w-48.rounded-md.shadow-lg ")) - [:a.hover:text-gray-300.text-gray-400.font-bold - {:href current-repo - :target "_blank"} - (string/capitalize (util/take-at-most (db/get-repo-name current-repo) 20))])))) + (ui/dropdown-with-links + (fn [{:keys [toggle-fn]}] + [:a.hover:text-gray-200.text-gray-500.font-bold {:on-click toggle-fn} + [:span (string/capitalize (util/take-at-most (db/get-repo-name current-repo) 20))] + [:span.dropdown-caret.ml-1 {:style {:border-top-color "#6b7280"}}]]) + (mapv + (fn [{:keys [id url]}] + {:title (db/get-repo-name url) + :options {:on-click (fn [] + (state/set-current-repo! url))}}) + (remove (fn [repo] + (= current-repo (:url repo))) + repos)) + (util/hiccup->class + "origin-top-right.absolute.left-0.mt-2.w-48.rounded-md.shadow-lg ")) + [:a.hover:text-gray-300.text-gray-400.font-bold + {:href current-repo + :target "_blank"} + (string/capitalize (util/take-at-most (db/get-repo-name current-repo) 20))])))) (defn nav-item [title href svg-d active? close-modal-fn] @@ -187,15 +188,7 @@ [:a.hover:text-gray-200.text-gray-500 {:style {:margin-right 13 :margin-top -1} - :on-click (fn [] - (d/add-class! (d/by-id "menu") - "md:block") - (d/remove-class! (d/by-id "left-sidebar") - "enter") - (d/remove-class! (d/by-id "search") - "sidebar-open") - (d/remove-class! (d/by-id "main") - "sidebar-open"))} + :on-click handler/hide-left-sidebar} (svg/menu "currentColor")] (repos current-repo close-fn) ]] @@ -212,7 +205,8 @@ me (state/sub :me) current-repo (state/sub :git/current-repo) status (db/sub-key-value :git/status) - pulling? (= :pulling status)] + pulling? (= :pulling status) + right-sidebar? false] [:div.h-screen.flex.overflow-hidden.bg-base-3 [:div.md:hidden [:div.fixed.inset-0.z-30.bg-gray-600.opacity-0.pointer-events-none.transition-opacity.ease-linear.duration-300 @@ -291,28 +285,38 @@ (when (:email me) {:title "Sign out" :options {:on-click handler/sign-out!}})] - (remove nil?)))]]] - [:main#main.flex-1.relative.z-0.overflow-y-scroll.py-6.focus:outline-none.sidebar-open - {:tabIndex "0"} - [:div.flex.justify-center - [:div.flex-1.m-6 {:style {:position "relative" - :max-width 700 - :margin-bottom 500}} - main-content]]] + (remove nil?))) + + [:a.hover:text-gray-900.text-gray-500.ml-3 + {:on-click (fn [] + (let [sidebar (d/by-id "right-sidebar")] + (if (d/has-class? sidebar "enter") + (handler/hide-right-sidebar) + (handler/show-right-sidebar))))} + (svg/menu)]]]] + [:main#main.flex-1.relative.z-0.py-6.focus:outline-none.sidebar-open.overflow-hidden + {:tabIndex "0" + :style {:width "100%" + :height "100%"}} + [:div#main-content + {:style {:width "100%" + :height "100%" + :overflow-y "scroll" + :padding-right 17 + :box-sizing "content-box"}} + [:div.flex.justify-center + [:div.flex-1.m-6#main-content-container + {:style {:position "relative" + :max-width 700 + :margin-bottom 200}} + main-content]]] + (right-sidebar/sidebar)] [:a#menu.mr-4.hover:text-gray-700.text-gray-500.absolute.hidden {:style {:position "absolute" :top 6 :left 16 :z-index 111} - :on-click (fn [] - (d/remove-class! (d/by-id "menu") - "md:block") - (d/add-class! (d/by-id "left-sidebar") - "enter") - (d/add-class! (d/by-id "search") - "sidebar-open") - (d/add-class! (d/by-id "main") - "sidebar-open"))} + :on-click handler/show-left-sidebar} (svg/menu "currentColor")] (ui/notification) (custom-context-menu)]])) diff --git a/web/src/main/frontend/components/svg.cljs b/web/src/main/frontend/components/svg.cljs index de6f79ff4..3cc09f9ef 100644 --- a/web/src/main/frontend/components/svg.cljs +++ b/web/src/main/frontend/components/svg.cljs @@ -29,6 +29,42 @@ {:d "M7.5 8l-5 5L1 11.5 4.75 8 1 4.5 2.5 3l5 5z", :fill-rule "evenodd"}]]) +(rum/defc big-arrow-right + [] + [:svg + {:fill "none", :view-box "0 0 24 24", :height "24", :width "24"} + [:path + {:stroke-linejoin "round", + :stroke-linecap "round", + :stroke-width "2", + :stroke "currentColor", + :d "M14 5L21 12M21 12L14 19M21 12L3 12"}]]) + +(rum/defc big-arrow-left + [] + [:svg + {:fill "none", :view-box "0 0 24 24", :height "24", :width "24"} + [:path + {:stroke-linejoin "round", + :stroke-linecap "round", + :stroke-width "2", + :stroke "currentColor", + :d "M10 19L3 12M3 12L10 5M3 12L21 12"}]]) + +(defn- hero-icon + [d] + [:svg + {:fill "none", :view-box "0 0 24 24", :height "24", :width "24"} + [:path + {:stroke-linejoin "round" + :stroke-linecap "round" + :stroke-width "2" + :stroke "currentColor" + :d d}]]) + +(def close (hero-icon "M6 18L18 6M6 6L18 18")) +(def plus (hero-icon "M12 4v16m8-8H4")) + (rum/defc caret-down [] [:svg diff --git a/web/src/main/frontend/handler.cljs b/web/src/main/frontend/handler.cljs index fb9aacc89..9131f4418 100644 --- a/web/src/main/frontend/handler.cljs +++ b/web/src/main/frontend/handler.cljs @@ -990,6 +990,44 @@ (p/let [_ (clone docs-repo)] (load-db-and-journals! docs-repo nil true))))) +;; sidebars +(defn hide-left-sidebar + [] + (dom/add-class! (dom/by-id "menu") + "md:block") + (dom/remove-class! (dom/by-id "left-sidebar") + "enter") + (dom/remove-class! (dom/by-id "search") + "sidebar-open") + (dom/remove-class! (dom/by-id "main") + "sidebar-open")) + +(defn show-left-sidebar + [] + (dom/remove-class! (dom/by-id "menu") + "md:block") + (dom/add-class! (dom/by-id "left-sidebar") + "enter") + (dom/add-class! (dom/by-id "search") + "sidebar-open") + (dom/add-class! (dom/by-id "main") + "sidebar-open")) + +(defn hide-right-sidebar + [] + (let [sidebar (dom/by-id "right-sidebar")] + (dom/remove-class! (dom/by-id "main-content-container") + "right-sidebar-open") + (dom/remove-class! sidebar "enter"))) + +(defn show-right-sidebar + [] + (let [sidebar (dom/by-id "right-sidebar")] + (hide-left-sidebar) + (dom/add-class! sidebar "enter") + (dom/add-class! (dom/by-id "main-content-container") + "right-sidebar-open"))) + (comment (defn debug-latest-commits diff --git a/web/src/main/frontend/state.cljs b/web/src/main/frontend/state.cljs index 05d3d695a..8a3e64327 100644 --- a/web/src/main/frontend/state.cljs +++ b/web/src/main/frontend/state.cljs @@ -2,7 +2,8 @@ (:require [frontend.storage :as storage] [rum.core :as rum] [frontend.util :as util] - [clojure.string :as string])) + [clojure.string :as string] + [medley.core :as medley])) ;; TODO: move git/latest-commit, git/status, git/error to corresponding datascript ;; dbs. @@ -45,6 +46,9 @@ ;; encrypted github token :encrypt/token (storage/get :encrypt/token) + + ;; pages or headings in the right sidebar + :sidebar/blocks '() })) (defn sub @@ -258,3 +262,16 @@ (when encrypted (set-state! :encrypt/token encrypted) (storage/set :encrypt/token encrypted))) + +(defn sidebar-add-block! + [block-type block-data] + (update-state! :sidebar/blocks #(cons [block-type block-data] %))) +(defn sidebar-remove-block! + [idx] + (update-state! :sidebar/blocks #(util/drop-nth idx %))) +(defn sidebar-clear! + [] + (set-state! :sidebar/blocks '())) +(defn get-sidebar-blocks + [] + (:sidebar/blocks @state)) diff --git a/web/src/main/frontend/ui.cljs b/web/src/main/frontend/ui.cljs index cb9e7a0a1..a690e89b3 100644 --- a/web/src/main/frontend/ui.cljs +++ b/web/src/main/frontend/ui.cljs @@ -154,7 +154,7 @@ ;; scroll (defn main-node [] - (first (array-seq (js/document.querySelectorAll "main")))) + (gdom/getElement "main-content")) (defn get-scroll-top [] (.-scrollTop (main-node))) diff --git a/web/src/main/frontend/util.cljs b/web/src/main/frontend/util.cljs index 057e9abf3..6a43d69cb 100644 --- a/web/src/main/frontend/util.cljs +++ b/web/src/main/frontend/util.cljs @@ -576,3 +576,6 @@ (defn uuid-string? [s] (re-find uuid-pattern s)) + +(defn drop-nth [n coll] + (keep-indexed #(if (not= %1 n) %2) coll))