icon.cljs 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190
  1. (ns frontend.components.icon
  2. (:require ["@emoji-mart/data" :as emoji-data]
  3. ["emoji-mart" :refer [SearchIndex]]
  4. [promesa.core :as p]
  5. [cljs-bean.core :as bean]
  6. [camel-snake-kebab.core :as csk]
  7. [clojure.string :as string]
  8. [frontend.search :as search]
  9. [rum.core :as rum]
  10. [frontend.ui :as ui]
  11. [frontend.util :as util]
  12. [goog.object :as gobj]
  13. [goog.functions :refer [debounce]]
  14. [frontend.config :as config]
  15. [frontend.handler.property.util :as pu]))
  16. (defn icon
  17. [icon & [opts]]
  18. (cond
  19. (and (= :emoji (:type icon)) (:id icon))
  20. [:em-emoji (merge {:id (:id icon)}
  21. opts)]
  22. (and (= :tabler-icon (:type icon)) (:id icon))
  23. (ui/icon (:id icon) opts)))
  24. (defn get-page-icon
  25. [page-entity opts]
  26. (let [default-icon (ui/icon "page" (merge opts {:extension? true}))
  27. page-icon (pu/get-block-property-value page-entity :icon)]
  28. (or
  29. (when-not (string/blank? page-icon)
  30. (icon page-icon opts))
  31. default-icon)))
  32. (defn- search-emojis
  33. [q]
  34. (p/let [result (.search SearchIndex q)]
  35. (bean/->clj result)))
  36. (defonce *tabler-icons (atom nil))
  37. (defn- get-tabler-icons
  38. []
  39. (if @*tabler-icons
  40. @*tabler-icons
  41. (let [result (->> (keys (bean/->clj js/tablerIcons))
  42. (map (fn [k]
  43. (-> (string/replace (csk/->Camel_Snake_Case (name k)) "_" " ")
  44. (string/replace-first "Icon " ""))))
  45. ;; FIXME: somehow those icons don't work
  46. (remove #{"Ab" "Ab 2" "Ab Off"}))]
  47. (reset! *tabler-icons result)
  48. result)))
  49. (def emojis
  50. (vals (bean/->clj (gobj/get emoji-data "emojis"))))
  51. (defn- search-tabler-icons
  52. [q]
  53. (search/fuzzy-search (get-tabler-icons) q :limit 100))
  54. (defn- search
  55. [q]
  56. (p/let [icons (search-tabler-icons q)
  57. emojis (search-emojis q)]
  58. {:icons icons
  59. :emojis emojis}))
  60. (rum/defc emoji-cp < rum/static
  61. [{:keys [id name] :as emoji} {:keys [on-chosen hover]}]
  62. [:button.text-2xl.w-9.h-9.transition-opacity
  63. {:tabIndex "0"
  64. :title name
  65. :on-click (fn [e]
  66. (on-chosen e {:type :emoji
  67. :id id
  68. :name name}))
  69. :on-mouse-over #(reset! hover emoji)
  70. :on-mouse-out #(reset! hover nil)}
  71. [:em-emoji {:id id}]])
  72. (rum/defc emojis-cp < rum/static
  73. [emojis opts]
  74. [:div.emojis.flex.flex-1.flex-row.gap-1.flex-wrap
  75. (for [emoji emojis]
  76. (rum/with-key (emoji-cp emoji opts) (:id emoji)))])
  77. (rum/defc icon-cp < rum/static
  78. [icon {:keys [on-chosen hover]}]
  79. [:button.w-9.h-9.transition-opacity
  80. {:key icon
  81. :tabIndex "0"
  82. :title icon
  83. :on-click (fn [e]
  84. (on-chosen e {:type :tabler-icon
  85. :id icon
  86. :name icon}))
  87. :on-mouse-over #(reset! hover {:type :tabler-icon
  88. :id icon
  89. :name icon
  90. :icon icon})
  91. :on-mouse-out #(reset! hover nil)}
  92. (ui/icon icon {:size 24})])
  93. (rum/defc icons-cp < rum/static
  94. [icons opts]
  95. [:div.icons.flex.flex-1.flex-row.gap-1.flex-wrap
  96. (for [icon icons]
  97. (icon-cp icon opts))])
  98. (rum/defcs icon-search <
  99. (rum/local "" ::q)
  100. (rum/local nil ::result)
  101. (rum/local :emoji ::tab)
  102. (rum/local nil ::hover)
  103. [state opts]
  104. (let [*q (::q state)
  105. *result (::result state)
  106. *tab (::tab state)
  107. *hover (::hover state)
  108. result @*result
  109. emoji-tab? (= @*tab :emoji)
  110. opts (assoc opts :hover *hover)]
  111. [:div.icon-search.flex.flex-1.flex-col.gap-2
  112. [:input.form-input.block.w-full.sm:text-sm.sm:leading-5
  113. {:auto-focus true
  114. :placeholder "Select icon"
  115. :default-value ""
  116. :on-change (debounce
  117. (fn [e]
  118. (reset! *q (util/evalue e))
  119. (if (string/blank? @*q)
  120. (reset! *result {})
  121. (p/let [result (search @*q)]
  122. (reset! *result result))))
  123. 200)}]
  124. [:div.search-result
  125. (if (seq result)
  126. [:div.flex.flex-1.flex-col.gap-1
  127. (when (seq (:emojis result))
  128. (emojis-cp (:emojis result) opts))
  129. (when (seq (:icons result))
  130. (icons-cp (:icons result) opts))]
  131. [:div.flex.flex-1.flex-col.gap-1
  132. [:div.flex.flex-1.flex-row.items-center.gap-2
  133. (ui/button
  134. "Emojis"
  135. {:intent "logseq"
  136. :small? true
  137. :on-click #(reset! *tab :emoji)})
  138. (ui/button
  139. "Icons"
  140. {:intent "logseq"
  141. :small? true
  142. :on-click #(reset! *tab :icon)})]
  143. (if emoji-tab?
  144. (emojis-cp emojis opts)
  145. (icons-cp (get-tabler-icons) opts))])]
  146. (if @*hover
  147. [:div.flex.flex-1.flex-row.items-center.gap-2
  148. [:button.transition-opacity
  149. {:style {:font-size 32}
  150. :key (:id @*hover)
  151. :title (:name @*hover)}
  152. (if (= :tabler-icon (:type @*hover))
  153. (ui/icon (:icon @*hover) {:size 32})
  154. (:native (first (:skins @*hover))))]
  155. (:name @*hover)]
  156. [:div {:style {:padding-bottom 32}}])]))
  157. (rum/defc icon-picker
  158. [icon-value {:keys [disabled? on-chosen]}]
  159. (ui/dropdown
  160. (fn [{:keys [toggle-fn]}]
  161. [:button.flex {:on-click #(when-not disabled? (toggle-fn))}
  162. (if icon-value
  163. (icon icon-value)
  164. [:span.bullet-container.cursor [:span.bullet]])])
  165. (if config/publishing?
  166. (constantly [])
  167. (fn [{:keys [toggle-fn]}]
  168. [:div.p-4
  169. (icon-search
  170. {:on-chosen (fn [e icon-value]
  171. (on-chosen e icon-value)
  172. (toggle-fn))})]))
  173. {:modal-class (util/hiccup->class
  174. "origin-top-right.absolute.left-0.rounded-md.shadow-lg")}))