list_item.cljs 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120
  1. (ns frontend.components.cmdk.list-item
  2. (:require
  3. ["remove-accents" :as remove-accents]
  4. [clojure.string :as string]
  5. [goog.string :as gstring]
  6. [logseq.shui.hooks :as hooks]
  7. [logseq.shui.ui :as shui]
  8. [rum.core :as rum]))
  9. (defn- to-string [input]
  10. (cond
  11. (string? input) input
  12. (keyword? input) (name input)
  13. (symbol? input) (name input)
  14. (number? input) (str input)
  15. (uuid? input) (str input)
  16. (nil? input) ""
  17. :else (pr-str input)))
  18. (defn- normalize-text [app-config text]
  19. (cond-> (to-string text)
  20. ;; :lower-case (string/lower-case)
  21. :normalize (.normalize "NFKC")
  22. (:feature/enable-search-remove-accents? app-config) (remove-accents)))
  23. (defn highlight-query* [app-config query text]
  24. (cond
  25. (or (vector? text) (object? text)) ; hiccup
  26. text
  27. (string/blank? query)
  28. [:span (to-string text)]
  29. :else
  30. (when-let [text-string (not-empty (to-string text))]
  31. (let [normal-text (normalize-text app-config text-string)
  32. normal-query (normalize-text app-config query)
  33. query-terms (string/replace (gstring/regExpEscape normal-query) #"\s+" "|")
  34. query-re (js/RegExp. (str "(" query-terms ")") "i")
  35. highlighted-text (string/replace normal-text query-re "<:hlmarker>$1<:hlmarker>")
  36. segs (string/split highlighted-text #"<:hlmarker>")]
  37. (if (seq segs)
  38. (into [:span {:aria-label text-string}]
  39. (map-indexed (fn [i seg]
  40. (if (even? i)
  41. [:span seg]
  42. [:span {:class "ui__list-item-highlighted-span"} seg]))
  43. segs))
  44. [:span normal-text])))))
  45. (rum/defc root [{:keys [group icon icon-theme query text info shortcut value-label value
  46. title highlighted on-highlight on-highlight-dep header on-click hls-page?
  47. hoverable compact rounded on-mouse-enter component-opts source-page] :as _props
  48. :or {hoverable true rounded true}}
  49. {:keys [app-config]}]
  50. (let [ref (hooks/create-ref)
  51. highlight-query (partial highlight-query* app-config query)
  52. [hover? set-hover?] (rum/use-state false)]
  53. (hooks/use-effect!
  54. (fn []
  55. (when (and highlighted on-highlight)
  56. (on-highlight ref)))
  57. [highlighted on-highlight-dep])
  58. [:div (merge
  59. {:style {:opacity (if highlighted 1 0.8)}
  60. :class (cond-> "flex flex-col transition-opacity"
  61. highlighted (str " !opacity-100 bg-gray-03-alpha dark:bg-gray-04-alpha")
  62. hoverable (str " transition-all duration-50 ease-in !opacity-75 hover:!opacity-100 hover:cursor-pointer hover:bg-gradient-to-r hover:from-gray-03-alpha hover:to-gray-01-alpha from-0% to-100%")
  63. (and hoverable rounded) (str " !rounded-lg")
  64. (not compact) (str " py-4 px-6 gap-1")
  65. compact (str " py-1.5 px-3 gap-0.5")
  66. (not highlighted) (str " "))
  67. :ref ref
  68. :on-click (when on-click on-click)
  69. :on-mouse-over #(set-hover? true)
  70. :on-mouse-out #(set-hover? false)
  71. :on-mouse-enter (when on-mouse-enter on-mouse-enter)}
  72. component-opts)
  73. ;; header
  74. (when header
  75. [:div.text-xs.pl-8.font-light {:class "-mt-1"
  76. :style {:color "var(--lx-gray-11)"}}
  77. (highlight-query header)])
  78. ;; main row
  79. [:div.flex.items-center.gap-3
  80. [:div.w-5.h-5.rounded.flex.items-center.justify-center
  81. {:style {:background (when (#{:gradient} icon-theme) "linear-gradient(-65deg, #8AE8FF, #5373E7, #369EFF, #00B1CC)")
  82. :box-shadow (when (#{:gradient} icon-theme) "inset 0 0 0 1px rgba(255,255,255,0.3) ")}
  83. :class (cond-> "w-5 h-5 rounded flex items-center justify-center"
  84. (= icon-theme :color) (str
  85. " "
  86. (if highlighted "bg-accent-07-alpha" "bg-gray-05")
  87. " dark:text-white")
  88. (= icon-theme :gray) (str " bg-gray-05 dark:text-white"))}
  89. (shui/tabler-icon icon {:size "14" :class ""})]
  90. [:div.flex.flex-1.flex-col
  91. (when title
  92. [:div.text-sm.pb-2.font-bold.text-gray-11 (highlight-query title)])
  93. [:div {:class "text-sm font-medium text-gray-12"}
  94. (if (and (= group :pages) (not= text source-page)) ;; alias
  95. [:div.flex.flex-row.items-center.gap-2
  96. (highlight-query text)
  97. (if-not hls-page?
  98. [:<> [:div.opacity-50.font-normal "alias of"] source-page]
  99. [:div.opacity-50.font-normal.text-xs " — Highlights page"])]
  100. (highlight-query text))
  101. (when info
  102. [:span.text-xs.text-gray-11 " — " (highlight-query info)])]]
  103. (when (or value-label value)
  104. [:div {:class "text-xs"}
  105. (when (and value-label value)
  106. [:span.text-gray-11 (str (to-string value-label) ": ")])
  107. (when (and value-label (not value))
  108. [:span.text-gray-11 (str (to-string value-label))])
  109. (when value
  110. [:span.text-gray-11 (to-string value)])])
  111. (when shortcut
  112. [:div {:class "flex gap-1"
  113. :style {:opacity (if (or highlighted hover?) 1 0.9)}}
  114. (shui/shortcut shortcut)])]]))