right_sidebar.cljs 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283
  1. (ns frontend.components.right-sidebar
  2. (:require [rum.core :as rum]
  3. [frontend.ui :as ui]
  4. [frontend.components.svg :as svg]
  5. [frontend.components.page :as page]
  6. [frontend.components.block :as block]
  7. [frontend.extensions.graph-2d :as graph-2d]
  8. [frontend.components.onboarding :as onboarding]
  9. [frontend.handler.route :as route-handler]
  10. [frontend.handler.page :as page-handler]
  11. [frontend.state :as state]
  12. [frontend.db :as db]
  13. [frontend.util :as util]
  14. [frontend.date :as date]
  15. [medley.core :as medley]
  16. [clojure.string :as string]
  17. [frontend.extensions.slide :as slide]
  18. [cljs-bean.core :as bean]
  19. [goog.object :as gobj]
  20. [frontend.graph :as graph]
  21. [frontend.context.i18n :as i18n]
  22. [reitit.frontend.easy :as rfe]
  23. [frontend.db-mixins :as db-mixins]
  24. [frontend.config :as config]))
  25. (rum/defc block-cp < rum/reactive
  26. [repo idx block]
  27. (let [id (:block/uuid block)]
  28. (page/page {:parameters {:path {:name (str id)}}
  29. :sidebar? true
  30. :sidebar/idx idx
  31. :repo repo})))
  32. (rum/defc page-cp < rum/reactive
  33. [repo page-name]
  34. (page/page {:parameters {:path {:name page-name}}
  35. :sidebar? true
  36. :repo repo}))
  37. (rum/defc page-graph < db-mixins/query
  38. [page]
  39. (let [theme (:ui/theme @state/state)
  40. dark? (= theme "dark")
  41. graph (if (util/uuid-string? page)
  42. (db/build-block-graph (uuid page) theme)
  43. (db/build-page-graph page theme))]
  44. (when (seq (:nodes graph))
  45. [:div.sidebar-item.flex-col.flex-1
  46. (graph-2d/graph
  47. (graph/build-graph-opts
  48. graph dark? false
  49. {:width 600
  50. :height 600}))])))
  51. (defn recent-pages
  52. []
  53. (let [pages (db/get-key-value :recent/pages)]
  54. [:div.recent-pages.text-sm.flex-col.flex.ml-3.mt-2
  55. (if (seq pages)
  56. (for [page pages]
  57. [:a.mb-1 {:key (str "recent-page-" page)
  58. :href (rfe/href :page {:name page})}
  59. page]))]))
  60. (rum/defcs foldable-list <
  61. (rum/local false ::fold?)
  62. [state page l]
  63. (let [fold? (get state ::fold?)]
  64. [:div
  65. [:div.flex.flex-row.items-center.mb-1
  66. [:a.control.opacity-50.hover:opacity-100
  67. {:on-click #(swap! fold? not)
  68. :style {:width "0.75rem"}}
  69. (when (seq l)
  70. (if @fold?
  71. svg/arrow-down-v2
  72. svg/arrow-right-v2))]
  73. [:a.ml-2 {:key (str "contents-" page)
  74. :href (rfe/href :page {:name page})}
  75. (util/capitalize-all page)]]
  76. (when (seq l)
  77. [:div.contents-list.ml-4 {:class (if @fold? "hidden" "initial")}
  78. (for [{:keys [page list]} l]
  79. (rum/with-key
  80. (foldable-list page list)
  81. (str "toc-item-" page)))])]))
  82. (rum/defc contents < rum/reactive db-mixins/query
  83. []
  84. [:div.contents.flex-col.flex.ml-3
  85. (when-let [contents (db/entity [:page/name "contents"])]
  86. (page/contents-page contents))])
  87. (defn build-sidebar-item
  88. [repo idx db-id block-type block-data t]
  89. (case block-type
  90. :contents
  91. [[:a {:on-click (fn [e]
  92. (util/stop e)
  93. (if-not (db/entity [:page/name "contents"])
  94. (page-handler/create! "contents")
  95. (route-handler/redirect! {:to :page
  96. :path-params {:name "contents"}})))}
  97. (t :right-side-bar/contents)]
  98. (contents)]
  99. :recent
  100. [(t :right-side-bar/recent) (recent-pages)]
  101. :help
  102. [(t :right-side-bar/help) (onboarding/help)]
  103. :page-graph
  104. [(str (t :right-side-bar/graph-ref) (util/capitalize-all block-data))
  105. (page-graph block-data)]
  106. :block-ref
  107. (when-let [block (db/entity repo [:block/uuid (:block/uuid (:block block-data))])]
  108. [(t :right-side-bar/block-ref)
  109. (let [block (:block block-data)
  110. block-id (:block/uuid block)
  111. format (:block/format block)]
  112. [[:div.ml-2.mt-1
  113. (block/block-parents repo block-id format)]
  114. [:div.ml-2
  115. (block-cp repo idx block)]])])
  116. :block
  117. (when-let [block (db/entity repo [:block/uuid (:block/uuid block-data)])]
  118. (let [block-id (:block/uuid block-data)
  119. format (:block/format block-data)]
  120. [(block/block-parents repo block-id format)
  121. [:div.ml-2
  122. (block-cp repo idx block-data)]]))
  123. :page
  124. (let [page-name (:page/name block-data)]
  125. [[:a {:href (rfe/href :page {:name (util/url-encode page-name)})}
  126. (util/capitalize-all page-name)]
  127. [:div.ml-2
  128. (page-cp repo page-name)]])
  129. :page-presentation
  130. (let [page-name (get-in block-data [:page :page/name])
  131. journal? (:journal? block-data)
  132. blocks (db/get-page-blocks repo page-name)
  133. blocks (if journal?
  134. (rest blocks)
  135. blocks)
  136. sections (block/build-slide-sections blocks {:id "slide-reveal-js"
  137. :start-level 2
  138. :slide? true
  139. :sidebar? true
  140. :page-name page-name})]
  141. [[:a {:href (str "/page/" (util/url-encode page-name))}
  142. (util/capitalize-all page-name)]
  143. [:div.ml-2.slide.mt-2
  144. (slide/slide sections)]])
  145. ["" [:span]]))
  146. (defn close
  147. ([on-close]
  148. (close nil on-close))
  149. ([class on-close]
  150. [:a.close.opacity-50.hover:opacity-100.flex.items-center
  151. (cond-> {:on-click on-close}
  152. class
  153. (assoc :class class))
  154. svg/close]))
  155. (rum/defc sidebar-item < rum/reactive
  156. [repo idx db-id block-type block-data t]
  157. (let [item
  158. (if (= :page block-type)
  159. (let [page (db/query-entity-in-component db-id)]
  160. (when (seq page)
  161. (build-sidebar-item repo idx db-id block-type page t)))
  162. (build-sidebar-item repo idx db-id block-type block-data t))]
  163. (when item
  164. (let [collapse? (state/sub [:ui/sidebar-collapsed-blocks db-id])]
  165. [:div.sidebar-item.content
  166. (let [[title component] item]
  167. [:div.flex.flex-col
  168. [:div.flex.flex-row.justify-between
  169. [:div.flex.flex-row.justify-center
  170. [:a.opacity-50.hover:opacity-100.flex.items-center.pr-1
  171. {:on-click #(state/sidebar-block-toggle-collapse! db-id)}
  172. (if collapse?
  173. (svg/caret-right)
  174. (svg/caret-down))]
  175. [:div.ml-1
  176. title]]
  177. (close #(state/sidebar-remove-block! idx))]
  178. [:div {:class (if collapse? "hidden" "initial")}
  179. component]])]))))
  180. (defn- get-page
  181. [match]
  182. (let [route-name (get-in match [:data :name])
  183. page (case route-name
  184. :page
  185. (get-in match [:path-params :name])
  186. :file
  187. (get-in match [:path-params :path])
  188. (date/journal-name))]
  189. (if page
  190. (util/url-decode (string/lower-case page)))))
  191. (defn get-current-page
  192. []
  193. (let [match (:route-match @state/state)
  194. theme (:ui/theme @state/state)]
  195. (get-page match)))
  196. (rum/defcs sidebar < rum/reactive
  197. [state]
  198. (let [blocks (state/sub :sidebar/blocks)
  199. sidebar-open? (state/sub :ui/sidebar-open?)
  200. repo (state/sub :git/current-repo)
  201. match (state/sub :route-match)
  202. theme (state/sub :ui/theme)
  203. dark? (= "dark" theme)
  204. t (i18n/use-tongue)]
  205. (rum/with-context [[t] i18n/*tongue-context*]
  206. [:div#right-sidebar.flex-col {:style {:height "100%"
  207. :overflow "hidden"
  208. :flex (if sidebar-open?
  209. "1 0 40%"
  210. "0 0 0px")}}
  211. (if sidebar-open?
  212. [:div.hide-scrollbar {:style {:flex "1 1 auto"
  213. :padding 12
  214. :height "100%"
  215. :overflow-y "auto"
  216. :overflow-x "hidden"
  217. :box-sizing "content-box"}}
  218. [:div.flex.flex-row.mb-2 {:key "right-sidebar-settings"}
  219. [:div.mr-4.text-sm
  220. [:a.right-sidebar-button {:on-click (fn [e]
  221. (state/sidebar-add-block! repo "contents" :contents nil))}
  222. (t :right-side-bar/contents)]]
  223. [:div.mr-4.text-sm
  224. [:a.right-sidebar-button {:on-click (fn [_e]
  225. (state/sidebar-add-block! repo "recent" :recent nil))}
  226. (t :right-side-bar/recent)]]
  227. (when config/publishing?
  228. [:div.mr-4.text-sm
  229. [:a {:href (rfe/href :all-pages)}
  230. (t :all-pages)]])
  231. [:div.mr-4.text-sm
  232. [:a.right-sidebar-button {:on-click (fn []
  233. (when-let [page (get-current-page)]
  234. (state/sidebar-add-block!
  235. repo
  236. (str "page-graph-" page)
  237. :page-graph
  238. page)))}
  239. (t :right-side-bar/page)]]
  240. [:div.mr-4.text-sm
  241. (let [theme (if dark? "white" "dark")]
  242. [:a.right-sidebar-button {:title (t :right-side-bar/switch-theme theme)
  243. :on-click (fn []
  244. (state/set-theme! theme))}
  245. (t :right-side-bar/theme (t (keyword theme)))])]
  246. (when-not config/publishing?
  247. [:div.mr-4.text-sm
  248. [:a.right-sidebar-button {:on-click (fn [_e]
  249. (state/sidebar-add-block! repo "help" :help nil))}
  250. (t :right-side-bar/help)]])]
  251. (for [[idx [repo db-id block-type block-data]] (medley/indexed blocks)]
  252. (rum/with-key
  253. (sidebar-item repo idx db-id block-type block-data t)
  254. (str "sidebar-block-" idx)))])])))