right_sidebar.cljs 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270
  1. (ns frontend.components.right-sidebar
  2. (:require [cljs-bean.core :as bean]
  3. [clojure.string :as string]
  4. [frontend.components.block :as block]
  5. [frontend.components.onboarding :as onboarding]
  6. [frontend.components.page :as page]
  7. [frontend.components.svg :as svg]
  8. [frontend.context.i18n :as i18n]
  9. [frontend.date :as date]
  10. [frontend.db :as db]
  11. [frontend.db-mixins :as db-mixins]
  12. [frontend.db.model :as db-model]
  13. [frontend.extensions.slide :as slide]
  14. [frontend.state :as state]
  15. [frontend.ui :as ui]
  16. [frontend.util :as util]
  17. [goog.object :as gobj]
  18. [medley.core :as medley]
  19. [reitit.frontend.easy :as rfe]
  20. [rum.core :as rum]))
  21. (rum/defc toggle
  22. []
  23. (when-not (util/mobile?)
  24. (ui/tippy
  25. {:html [:div.text-sm.font-medium
  26. (ui/keyboard-shortcut-from-config :ui/toggle-right-sidebar)]
  27. :position "left"
  28. :theme "monospace"
  29. :interactive true
  30. :arrow true}
  31. [:a.button.fade-link.toggle
  32. {:on-click state/toggle-sidebar-open?!}
  33. (ui/icon "layout-sidebar-right" {:style {:fontSize "20px"}})])))
  34. (rum/defc block-cp < rum/reactive
  35. [repo idx block]
  36. (let [id (:block/uuid block)]
  37. (page/page {:parameters {:path {:name (str id)}}
  38. :sidebar? true
  39. :sidebar/idx idx
  40. :repo repo})))
  41. (rum/defc page-cp < rum/reactive
  42. [repo page-name]
  43. (page/page {:parameters {:path {:name page-name}}
  44. :sidebar? true
  45. :repo repo}))
  46. (rum/defc contents < rum/reactive db-mixins/query
  47. []
  48. [:div.contents.flex-col.flex.ml-3
  49. (when-let [contents (db/entity [:block/name "contents"])]
  50. (page/contents-page contents))])
  51. (defn build-sidebar-item
  52. [repo idx db-id block-type block-data t]
  53. (case block-type
  54. :contents
  55. [(t :right-side-bar/contents)
  56. (contents)]
  57. :help
  58. [(t :right-side-bar/help) (onboarding/help)]
  59. :page-graph
  60. [(str (t :right-side-bar/page-graph))
  61. (page/page-graph)]
  62. :block-ref
  63. (when-let [block (db/entity repo [:block/uuid (:block/uuid (:block block-data))])]
  64. [(t :right-side-bar/block-ref)
  65. (let [block (:block block-data)
  66. block-id (:block/uuid block)
  67. format (:block/format block)]
  68. [[:div.ml-2.mt-1
  69. (block/block-parents {:id "block-parent"
  70. :block? true} repo block-id {})]
  71. [:div.ml-2
  72. (block-cp repo idx block)]])])
  73. :block
  74. (when-let [block (db/entity repo [:block/uuid (:block/uuid block-data)])]
  75. (let [block-id (:block/uuid block-data)
  76. format (:block/format block-data)]
  77. [(block/block-parents {:id "block-parent"
  78. :block? true} repo block-id {})
  79. [:div.ml-2
  80. (block-cp repo idx block-data)]]))
  81. :page
  82. (let [page-name (or (:block/name block-data)
  83. db-id)
  84. page-name (if (integer? db-id)
  85. (:block/name (db/entity db-id))
  86. page-name)]
  87. [[:a.page-title {:href (rfe/href :page {:name page-name})
  88. :on-click (fn [e]
  89. (when (gobj/get e "shiftKey")
  90. (.preventDefault e)))}
  91. (db-model/get-page-original-name page-name)]
  92. [:div.ml-2
  93. (page-cp repo page-name)]])
  94. :page-presentation
  95. (let [page-name (get-in block-data [:page :block/name])
  96. journal? (:journal? block-data)
  97. blocks (db/get-page-blocks repo page-name)
  98. blocks (if journal?
  99. (rest blocks)
  100. blocks)
  101. sections (block/build-slide-sections blocks {:id "slide-reveal-js"
  102. :slide? true
  103. :sidebar? true
  104. :page-name page-name})]
  105. [[:a {:href (rfe/href :page {:name page-name})}
  106. (db-model/get-page-original-name page-name)]
  107. [:div.ml-2.slide.mt-2
  108. (slide/slide sections)]])
  109. ["" [:span]]))
  110. (defn close
  111. ([on-close]
  112. (close nil on-close))
  113. ([class on-close]
  114. [:a.close.opacity-50.hover:opacity-100.flex.items-center
  115. (cond-> {:on-click on-close
  116. :style {:margin-right -4}}
  117. class
  118. (assoc :class class))
  119. svg/close]))
  120. (rum/defc sidebar-item < rum/reactive
  121. [repo idx db-id block-type block-data t]
  122. (let [item
  123. (if (= :page block-type)
  124. (let [lookup-ref (if (number? db-id) db-id [:block/name (string/lower-case db-id)])
  125. page (db/query-entity-in-component lookup-ref)]
  126. (when (seq page)
  127. (build-sidebar-item repo idx db-id block-type page t)))
  128. (build-sidebar-item repo idx db-id block-type block-data t))]
  129. (when item
  130. (let [collapse? (state/sub [:ui/sidebar-collapsed-blocks db-id])]
  131. [:div.sidebar-item.content.color-level.px-4.shadow-lg
  132. (let [[title component] item]
  133. [:div.flex.flex-col
  134. [:div.flex.flex-row.justify-between
  135. [:div.flex.flex-row.justify-center
  136. [:a.opacity-50.hover:opacity-100.flex.items-center.pr-1
  137. {:on-click #(state/sidebar-block-toggle-collapse! db-id)}
  138. (ui/rotating-arrow collapse?)]
  139. [:div.ml-1.font-medium
  140. title]]
  141. (close #(state/sidebar-remove-block! idx))]
  142. [:div {:class (if collapse? "hidden" "initial")}
  143. component]])]))))
  144. (defn- get-page
  145. [match]
  146. (let [route-name (get-in match [:data :name])
  147. page (case route-name
  148. :page
  149. (get-in match [:path-params :name])
  150. :file
  151. (get-in match [:path-params :path])
  152. (date/journal-name))]
  153. (when page
  154. (string/lower-case page))))
  155. (defn get-current-page
  156. []
  157. (let [match (:route-match @state/state)
  158. theme (:ui/theme @state/state)]
  159. (get-page match)))
  160. (rum/defc sidebar-resizer
  161. []
  162. (let [el-ref (rum/use-ref nil)]
  163. (rum/use-effect!
  164. (fn []
  165. (when-let [el (and (fn? js/window.interact) (rum/deref el-ref))]
  166. (-> (js/interact el)
  167. (.draggable
  168. (bean/->js
  169. {:listeners
  170. {:move
  171. (fn [^js/MouseEvent e]
  172. (let [width js/document.documentElement.clientWidth
  173. offset (.-left (.-rect e))
  174. right-el-ratio (- 1 (.toFixed (/ offset width) 6))
  175. right-el-ratio (cond
  176. (< right-el-ratio 0.2) 0.2
  177. (> right-el-ratio 0.7) 0.7
  178. :else right-el-ratio)
  179. right-el (js/document.getElementById "right-sidebar")]
  180. (when right-el
  181. (let [width (str (* right-el-ratio 100) "%")]
  182. (.setProperty (.-style right-el) "width" width)))))}}))
  183. (.styleCursor false)
  184. (.on "dragstart" #(.. js/document.documentElement -classList (add "is-resizing-buf")))
  185. (.on "dragend" #(.. js/document.documentElement -classList (remove "is-resizing-buf")))))
  186. #())
  187. [])
  188. [:span.resizer {:ref el-ref}]))
  189. (rum/defcs sidebar-inner <
  190. (rum/local false ::anim-finished?)
  191. {:will-mount (fn [state]
  192. (js/setTimeout (fn [] (reset! (get state ::anim-finished?) true)) 300)
  193. state)}
  194. [state repo t blocks]
  195. (let [*anim-finished? (get state ::anim-finished?)]
  196. [:div.cp__right-sidebar-inner.flex.flex-col.h-full#right-sidebar-container
  197. (sidebar-resizer)
  198. [:div.cp__right-sidebar-scrollable
  199. [:div.cp__right-sidebar-topbar.flex.flex-row.justify-between.items-center.pl-4.pr-2.h-12
  200. [:div.cp__right-sidebar-settings.hide-scrollbar {:key "right-sidebar-settings"}
  201. [:div.ml-4.text-sm
  202. [:a.cp__right-sidebar-settings-btn {:on-click (fn [e]
  203. (state/sidebar-add-block! repo "contents" :contents nil))}
  204. (t :right-side-bar/contents)]]
  205. [:div.ml-4.text-sm
  206. [:a.cp__right-sidebar-settings-btn {:on-click (fn []
  207. (when-let [page (get-current-page)]
  208. (state/sidebar-add-block!
  209. repo
  210. (str "page-graph-" page)
  211. :page-graph
  212. page)))}
  213. (t :right-side-bar/page)]]
  214. [:div.ml-4.text-sm
  215. [:a.cp__right-sidebar-settings-btn {:on-click (fn [_e]
  216. (state/sidebar-add-block! repo "help" :help nil))}
  217. (t :right-side-bar/help)]]]
  218. [:div.flex.align-items {:style {:z-index 999
  219. :margin-right 2}}
  220. (toggle)]]
  221. [:.sidebar-item-list.flex-1.scrollbar-spacing
  222. (if @*anim-finished?
  223. (for [[idx [repo db-id block-type block-data]] (medley/indexed blocks)]
  224. (rum/with-key
  225. (sidebar-item repo idx db-id block-type block-data t)
  226. (str "sidebar-block-" idx)))
  227. [:div.p-4
  228. [:span.font-medium.opacity-50 "Loading ..."]])]]]))
  229. (rum/defcs sidebar < rum/reactive
  230. [state]
  231. (let [blocks (state/sub :sidebar/blocks)
  232. blocks (if (empty? blocks)
  233. [[(state/get-current-repo) "contents" :contents nil]]
  234. blocks)
  235. sidebar-open? (state/sub :ui/sidebar-open?)
  236. repo (state/sub :git/current-repo)
  237. t (i18n/use-tongue)]
  238. (rum/with-context [[t] i18n/*tongue-context*]
  239. [:div#right-sidebar.cp__right-sidebar.h-screen
  240. {:class (if sidebar-open? "open" "closed")}
  241. (when sidebar-open?
  242. (sidebar-inner repo t blocks))])))