select.cljs 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123
  1. (ns frontend.components.select
  2. "Generic component for fuzzy searching items to select an item. See
  3. select-config to add a new use or select-type for this component. To use the
  4. new select-type, set :ui/open-select to the select-type. See
  5. :graph/open command for an example."
  6. (:require [frontend.modules.shortcut.core :as shortcut]
  7. [frontend.context.i18n :refer [t]]
  8. [frontend.search :as search]
  9. [frontend.state :as state]
  10. [frontend.ui :as ui]
  11. [frontend.util :as util]
  12. [frontend.db :as db]
  13. [logseq.graph-parser.text :as text]
  14. [rum.core :as rum]
  15. [frontend.config :as config]
  16. [frontend.handler.repo :as repo-handler]
  17. [reitit.frontend.easy :as rfe]))
  18. (rum/defc render-item
  19. [{:keys [id value]} chosen?]
  20. [:div.inline-grid.grid-cols-4.gap-x-4.w-full
  21. {:class (when chosen? "chosen")}
  22. [:span.col-span-3 value]
  23. [:div.col-span-1.justify-end.tip.flex
  24. (when id
  25. [:code.opacity-20.bg-transparent id])]])
  26. (rum/defcs select <
  27. (shortcut/disable-all-shortcuts)
  28. (rum/local "" ::input)
  29. {:will-unmount (fn [state]
  30. (state/set-state! [:ui/open-select] nil)
  31. state)}
  32. [state {:keys [items limit on-chosen empty-placeholder prompt-key]
  33. :or {limit 100
  34. prompt-key :select/default-prompt
  35. empty-placeholder (fn [_t] [:div])}}]
  36. (let [input (::input state)]
  37. [:div.cp__select.cp__select-main
  38. [:div.input-wrap
  39. [:input.cp__select-input.w-full
  40. {:type "text"
  41. :placeholder (t prompt-key)
  42. :auto-focus true
  43. :value @input
  44. :on-change (fn [e] (reset! input (util/evalue e)))}]]
  45. [:div.item-results-wrap
  46. (ui/auto-complete
  47. (search/fuzzy-search items @input :limit limit :extract-fn :value)
  48. {:item-render render-item
  49. :class "cp__select-results"
  50. :on-chosen (fn [x]
  51. (state/close-modal!)
  52. (on-chosen x))
  53. :empty-placeholder (empty-placeholder t)})]]))
  54. (defn select-config
  55. "Config that supports multiple types (uses) of this component. To add a new
  56. type, add a key with the value being a map with the following keys:
  57. * :items-fn - fn that returns items with a :value key that are used for the
  58. fuzzy search and selection. Items can have an optional :id and are displayed
  59. lightly for a given item.
  60. * :on-chosen - fn that is given item when it is chosen.
  61. * :empty-placeholder (optional) - fn that returns hiccup html to render if no
  62. matched graphs found.
  63. * :prompt-key (optional) - dictionary keyword that prompts when components is
  64. first open. Defaults to :select/default-prompt."
  65. []
  66. {:graph-open
  67. {:items-fn (fn []
  68. (->>
  69. (state/get-repos)
  70. (remove (fn [{:keys [url]}]
  71. (or (config/demo-graph? url)
  72. (= url (state/get-current-repo)))))
  73. (map (fn [{:keys [url]}]
  74. {:value (text/get-graph-name-from-path
  75. ;; TODO: Use helper when a common one is refactored
  76. ;; from components.repo
  77. (if (config/local-db? url)
  78. (config/get-local-dir url)
  79. (db/get-repo-path url)))
  80. :id (config/get-repo-dir url)
  81. :graph url}))))
  82. :prompt-key :select.graph/prompt
  83. :on-chosen #(state/pub-event! [:graph/switch (:graph %)])
  84. :empty-placeholder (fn [t]
  85. [:div.px-4.py-2
  86. [:div.mb-2 (t :select.graph/empty-placeholder-description)]
  87. (ui/button
  88. (t :select.graph/add-graph)
  89. :href (rfe/href :repo-add)
  90. :on-click state/close-modal!)])}
  91. :graph-remove
  92. {:items-fn (fn []
  93. (->> (state/get-repos)
  94. (remove (fn [{:keys [url]}]
  95. (config/demo-graph? url)))
  96. (map (fn [{:keys [url] :as original-graph}]
  97. {:value (text/get-graph-name-from-path
  98. ;; TODO: Use helper when a common one is refactored
  99. ;; from components.repo
  100. (if (config/local-db? url)
  101. (config/get-local-dir url)
  102. (db/get-repo-path url)))
  103. :id (config/get-repo-dir url)
  104. :graph url
  105. :original-graph original-graph}))))
  106. :on-chosen #(repo-handler/remove-repo! (:original-graph %))}})
  107. (rum/defc select-modal < rum/reactive
  108. []
  109. (when-let [select-type (state/sub [:ui/open-select])]
  110. (let [select-type-config (get (select-config) select-type)]
  111. (state/set-modal!
  112. #(select (-> select-type-config
  113. (select-keys [:on-chosen :empty-placeholder :prompt-key])
  114. (assoc :items ((:items-fn select-type-config)))))
  115. {:fullscreen? false
  116. :close-btn? false}))
  117. nil))