tldraw.cljs 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140
  1. (ns frontend.extensions.tldraw
  2. "Adapters related to tldraw"
  3. (:require ["/frontend/tldraw-logseq" :as TldrawLogseq]
  4. [frontend.components.block :as block]
  5. [frontend.components.page :as page]
  6. [frontend.db.model :as model]
  7. [frontend.handler.editor :as editor-handler]
  8. [frontend.handler.route :as route-handler]
  9. [frontend.handler.whiteboard :as whiteboard-handler]
  10. [frontend.rum :as r]
  11. [frontend.search :as search]
  12. [frontend.state :as state]
  13. [frontend.util :as util]
  14. [goog.object :as gobj]
  15. [promesa.core :as p]
  16. [rum.core :as rum]
  17. [frontend.ui :as ui]
  18. [frontend.components.whiteboard :as whiteboard]))
  19. (def tldraw (r/adapt-class (gobj/get TldrawLogseq "App")))
  20. (def generate-preview (gobj/get TldrawLogseq "generateJSXFromModel"))
  21. (rum/defc page-cp
  22. [props]
  23. (page/page {:page-name (gobj/get props "pageName") :whiteboard? true}))
  24. (rum/defc block-cp
  25. [props]
  26. ((state/get-component :block/single-block) (uuid (gobj/get props "blockId"))))
  27. (rum/defc breadcrumb
  28. [props]
  29. (block/breadcrumb {:preview? true}
  30. (state/get-current-repo)
  31. (uuid (gobj/get props "blockId"))
  32. {:end-separator? (gobj/get props "endSeparator")
  33. :level-limit (gobj/get props "levelLimit" 3)}))
  34. (rum/defc block-reference
  35. [props]
  36. (block/block-reference {} (gobj/get props "blockId") nil))
  37. (rum/defc page-name-link
  38. [props]
  39. (block/page-cp {:preview? true} {:block/name (gobj/get props "pageName")}))
  40. (defn search-handler
  41. [q filters]
  42. (let [{:keys [pages? blocks? files?]} (js->clj filters {:keywordize-keys true})
  43. repo (state/get-current-repo)
  44. limit 100]
  45. (p/let [blocks (when blocks? (search/block-search repo q {:limit limit}))
  46. pages (when pages? (search/page-search q))
  47. files (when files? (search/file-search q limit))]
  48. (clj->js {:pages pages :blocks blocks :files files}))))
  49. (defn save-asset-handler
  50. [file]
  51. (-> (editor-handler/save-assets! nil (state/get-current-repo) [(js->clj file)])
  52. (p/then
  53. (fn [res]
  54. (when-let [[asset-file-name _ full-file-path] (and (seq res) (first res))]
  55. (editor-handler/resolve-relative-path (or full-file-path asset-file-name)))))))
  56. (defn references-count
  57. [props]
  58. (apply whiteboard/references-count
  59. (map (fn [k] (js->clj (gobj/get props k) {:keywordize-keys true})) ["id" "className" "options"])))
  60. (def tldraw-renderers {:Page page-cp
  61. :Block block-cp
  62. :Breadcrumb breadcrumb
  63. :PageName page-name-link
  64. :BacklinksCount references-count
  65. :BlockReference block-reference})
  66. (defn get-tldraw-handlers [current-whiteboard-name]
  67. {:search search-handler
  68. :queryBlockByUUID #(clj->js (model/query-block-by-uuid (parse-uuid %)))
  69. :getBlockPageName #(:block/name (model/get-block-page (state/get-current-repo) (parse-uuid %)))
  70. :isWhiteboardPage model/whiteboard-page?
  71. :saveAsset save-asset-handler
  72. :makeAssetUrl editor-handler/make-asset-url
  73. :addNewWhiteboard (fn [page-name]
  74. (whiteboard-handler/create-new-whiteboard-page! page-name))
  75. :addNewBlock (fn [content]
  76. (str (whiteboard-handler/add-new-block! current-whiteboard-name content)))
  77. :sidebarAddBlock (fn [uuid type]
  78. (state/sidebar-add-block! (state/get-current-repo)
  79. (:db/id (model/get-page uuid))
  80. (keyword type)))
  81. :redirectToPage (fn [page-name-or-uuid]
  82. (let [page-name (or (when (util/uuid-string? page-name-or-uuid)
  83. (:block/name (model/get-block-page (state/get-current-repo)
  84. (parse-uuid page-name-or-uuid))))
  85. page-name-or-uuid)
  86. page-exists? (model/page-exists? page-name)
  87. whiteboard? (model/whiteboard-page? page-name)]
  88. (when page-exists?
  89. (if whiteboard? (route-handler/redirect-to-whiteboard!
  90. page-name {:block-id page-name-or-uuid})
  91. (route-handler/redirect-to-page! page-name-or-uuid)))))})
  92. (rum/defc tldraw-app
  93. [page-name block-id]
  94. (let [populate-onboarding? (whiteboard-handler/should-populate-onboarding-whiteboard? page-name)
  95. data (whiteboard-handler/page-name->tldr! page-name)
  96. [loaded-app set-loaded-app] (rum/use-state nil)
  97. on-mount (fn [tln]
  98. (when-let [^js api (gobj/get tln "api")]
  99. (p/then (when populate-onboarding?
  100. (whiteboard-handler/populate-onboarding-whiteboard api))
  101. #(do (state/focus-whiteboard-shape tln block-id)
  102. (set-loaded-app tln)))))]
  103. (rum/use-effect! (fn [] (when (and loaded-app block-id)
  104. (state/focus-whiteboard-shape loaded-app block-id)) #())
  105. [block-id loaded-app])
  106. (when data
  107. [:div.draw.tldraw.whiteboard.relative.w-full.h-full
  108. {:style {:overscroll-behavior "none"}
  109. :on-blur (fn [e]
  110. (when (#{"INPUT" "TEXTAREA"} (.-tagName (gobj/get e "target")))
  111. (state/clear-edit!)))
  112. ;; wheel -> overscroll may cause browser navigation
  113. :on-wheel util/stop-propagation}
  114. (when
  115. (and populate-onboarding? (not loaded-app))
  116. [:div.absolute.inset-0.flex.items-center.justify-center
  117. {:style {:z-index 200}}
  118. (ui/loading "Loading onboarding whiteboard ...")])
  119. (tldraw {:renderers tldraw-renderers
  120. :handlers (get-tldraw-handlers page-name)
  121. :onMount on-mount
  122. :onPersist (fn [app]
  123. (let [document (gobj/get app "serialized")]
  124. (whiteboard-handler/transact-tldr! page-name document)))
  125. :model data})])))