page.cljs 9.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219
  1. (ns frontend.components.db-based.page
  2. "Page components only for DB graphs"
  3. (:require [frontend.components.block :as component-block]
  4. [frontend.components.editor :as editor]
  5. [frontend.components.class :as class-component]
  6. [frontend.components.property.value :as pv]
  7. [frontend.components.icon :as icon-component]
  8. [frontend.config :as config]
  9. [frontend.db :as db]
  10. [frontend.handler.db-based.property :as db-property-handler]
  11. [frontend.ui :as ui]
  12. [frontend.state :as state]
  13. [rum.core :as rum]
  14. [logseq.shui.ui :as shui]
  15. [frontend.util :as util]
  16. [clojure.set :as set]
  17. [clojure.string :as string]))
  18. (rum/defc page-properties < rum/reactive
  19. [page {:keys [mode configure?]}]
  20. (let [class? (= mode :class)
  21. edit-input-id-prefix (str "edit-block-" (:block/uuid page))
  22. configure-opts {:selected? false
  23. :page-configure? true}
  24. has-viewable-properties? (db-property-handler/block-has-viewable-properties? page)
  25. has-class-properties? (seq (:class/schema.properties page))
  26. has-tags? (seq (:block/tags page))
  27. hide-properties? (get-in page [:block/properties (:block/uuid (db/entity :logseq.property/hide-properties?))])]
  28. (when (or configure?
  29. (and
  30. (not hide-properties?)
  31. (or has-viewable-properties?
  32. has-class-properties?
  33. has-tags?)))
  34. [:div.ls-page-properties
  35. {:class (util/classnames [{:no-mode (nil? mode)
  36. :no-properties (if class?
  37. (not has-class-properties?)
  38. (not has-viewable-properties?))}])}
  39. (cond
  40. (= mode :class)
  41. (if (and config/publishing? (not configure?))
  42. (component-block/db-properties-cp {:editor-box editor/box}
  43. page
  44. (str edit-input-id-prefix "-page")
  45. (assoc configure-opts :class-schema? false))
  46. (component-block/db-properties-cp {:editor-box editor/box}
  47. page
  48. (str edit-input-id-prefix "-schema")
  49. (assoc configure-opts :class-schema? true)))
  50. (= mode :page)
  51. (component-block/db-properties-cp {:editor-box editor/box}
  52. page
  53. (str edit-input-id-prefix "-page")
  54. (assoc configure-opts :class-schema? false :page? true)))])))
  55. (rum/defc icon-row < rum/reactive
  56. [page]
  57. [:div.grid.grid-cols-5.gap-1.items-center
  58. [:label.col-span-2 "Icon:"]
  59. (let [icon-value (:logseq.property/icon page)]
  60. [:div.col-span-3.flex.flex-row.items-center.gap-2
  61. (icon-component/icon-picker icon-value
  62. {:disabled? config/publishing?
  63. :on-chosen (fn [_e icon]
  64. (db-property-handler/set-block-property!
  65. (state/get-current-repo)
  66. (:db/id page)
  67. :logseq.property/icon
  68. icon
  69. {}))})
  70. (when (and icon-value (not config/publishing?))
  71. [:a.fade-link.flex {:on-click (fn [_e]
  72. (db-property-handler/remove-block-property!
  73. (state/get-current-repo)
  74. (:db/id page)
  75. :logseq.property/icon))
  76. :title "Delete this icon"}
  77. (ui/icon "X")])])])
  78. (rum/defc tags
  79. [page]
  80. (let [tags-property (db/entity :block/tags)]
  81. (pv/property-value page tags-property
  82. (map :db/id (:block/tags page))
  83. {:page-cp (fn [config page]
  84. (component-block/page-cp (assoc config :tag? true) page))
  85. :inline-text component-block/inline-text})))
  86. (rum/defc tags-row < rum/reactive
  87. [page]
  88. [:div.grid.grid-cols-5.gap-1.items-center
  89. [:label.col-span-2 "Tags:"]
  90. [:div.col-span-3.flex.flex-row.items-center.gap-2
  91. (tags page)]])
  92. (rum/defcs page-configure < rum/reactive
  93. [state page *mode]
  94. (let [*mode *mode
  95. mode (rum/react *mode)
  96. types (:block/type page)
  97. class? (contains? types "class")
  98. page-opts {:configure? true}]
  99. (when (nil? mode)
  100. (reset! *mode (cond
  101. class? :class
  102. :else :page)))
  103. [:div.flex.flex-col.gap-1
  104. [:<>
  105. (when (= mode :class)
  106. (class-component/configure page {:show-title? false}))
  107. (when-not config/publishing? (tags-row page))
  108. (when-not config/publishing? (icon-row page))
  109. [:h2 "Properties: "]
  110. (page-properties page (assoc page-opts :mode mode))]]))
  111. (rum/defc page-properties-react < rum/reactive
  112. [page* page-opts]
  113. (let [page (db/sub-block (:db/id page*))]
  114. (when (or (db-property-handler/block-has-viewable-properties? page)
  115. ;; Allow class and property pages to add new property
  116. (some #{"class" "property"} (:block/type page)))
  117. (page-properties page page-opts))))
  118. (rum/defc mode-switch < rum/reactive
  119. [types *mode]
  120. (let [current-mode (rum/react *mode)
  121. class? (contains? types "class")
  122. modes (->
  123. (cond
  124. class?
  125. ["Class"]
  126. :else
  127. [])
  128. (conj "Page"))]
  129. [:div.flex.flex-row.items-center.gap-1
  130. (for [mode modes]
  131. (let [mode' (keyword (string/lower-case mode))
  132. selected? (and (= mode' current-mode) (> (count modes) 1))]
  133. (shui/button {:class (when-not selected? "opacity-70")
  134. :variant (if selected? :outline :ghost)
  135. :size :sm
  136. :on-click (fn [e]
  137. (util/stop-propagation e)
  138. (reset! *mode mode'))}
  139. mode)))]))
  140. (rum/defcs page-info < rum/reactive
  141. (rum/local false ::hover?)
  142. (rum/local nil ::mode)
  143. {:init (fn [state]
  144. (assoc state ::collapsed? (atom true)))}
  145. [state page *hover-title?]
  146. (let [page (db/sub-block (:db/id page))
  147. *collapsed? (::collapsed? state)
  148. *hover? (::hover? state)
  149. *mode (::mode state)
  150. types (:block/type page)
  151. class? (contains? types "class")
  152. hover-title? (rum/react *hover-title?)
  153. collapsed? (rum/react *collapsed?)
  154. has-tags? (seq (:block/tags page))
  155. has-properties? (seq (:block/properties page))
  156. hover-or-expanded? (or @*hover? hover-title? (not collapsed?))
  157. show-info? (or hover-or-expanded? has-tags? has-properties? class?)]
  158. (when (if config/publishing?
  159. ;; Since publishing is read-only, hide this component if it has no info to show
  160. ;; as it creates a fair amount of empty vertical space
  161. (or has-tags? (some? types))
  162. true)
  163. [:div.page-info
  164. {:class (util/classnames [{:is-collapsed collapsed?}])}
  165. [:div.py-2 {:class (if (or @*hover? (not collapsed?))
  166. "border rounded"
  167. "border rounded border-transparent")}
  168. [:div.info-title.cursor
  169. {:on-mouse-over #(reset! *hover? true)
  170. :on-mouse-leave #(when-not (state/dropdown-opened?)
  171. (reset! *hover? false))
  172. :on-click (if config/publishing?
  173. (fn [_]
  174. (when (seq (set/intersection #{"class" "property"} types))
  175. (swap! *collapsed? not)))
  176. #(swap! *collapsed? not))}
  177. (when show-info?
  178. [:<>
  179. [:div.flex.flex-row.items-center.gap-2
  180. (if collapsed?
  181. (if (or has-tags? @*hover? config/publishing?)
  182. [:<>
  183. (if has-tags?
  184. [:div.px-1 {:style {:min-height 28}}]
  185. [:a.flex.fade-link.ml-2 (ui/icon "tags")])
  186. (if (and config/publishing? (seq (set/intersection #{"class" "property"} types)))
  187. [:div
  188. [:div.opacity-50.pointer.text-sm "Expand for more info"]]
  189. [:div {:on-click util/stop-propagation}
  190. (tags page)])]
  191. [:div.page-info-title-placeholder])
  192. [:div.flex.flex-row.items-center.gap-1
  193. [:a.flex.fade-link.ml-3 (ui/icon "info-circle")]
  194. (mode-switch types *mode)])]
  195. (when (or @*hover? (not collapsed?))
  196. [:div.px-1.absolute.right-0.top-0
  197. (shui/button
  198. {:variant :ghost :size :sm}
  199. (if collapsed?
  200. [:span.opacity-80.flex.items-center
  201. (ui/icon "adjustments-horizontal" {:size 16})]
  202. (ui/icon "x")))])])]
  203. (when show-info?
  204. (if collapsed?
  205. (when (or (seq (:block/properties page))
  206. (and class? (seq (:class/schema.properties page))))
  207. [:div.px-4
  208. (page-properties page {:mode (if class? :class :page)})])
  209. [:div.pt-2.px-4
  210. (page-configure page *mode)]))]])))