Просмотр исходного кода

enhance(ui): WIP enhance the multi select component for shui

charlie 1 год назад
Родитель
Сommit
1e9842733c
2 измененных файлов с 131 добавлено и 49 удалено
  1. 67 20
      deps/shui/src/logseq/shui/demo2.cljs
  2. 64 29
      deps/shui/src/logseq/shui/select/multi.cljs

+ 67 - 20
deps/shui/src/logseq/shui/demo2.cljs

@@ -5,7 +5,6 @@
             [logseq.shui.select.multi :refer [x-select]]
             [frontend.components.icon :refer [emojis-cp emojis icon-search]]))
 
-
 (rum/defc page
   []
 
@@ -16,15 +15,15 @@
    (let [items [{:key 1 :value "Apple" :class "bg-gray-800 text-gray-50"}
                 {:key 2 :value "Orange" :class "bg-orange-700 text-gray-50"}
                 nil
-                {:key 3 :value  "Pear"}]
+                {:key 3 :value "Pear"}]
 
          [selected-items set-selected-items!]
          (rum/use-state [(last items)])
 
          rm-item! (fn [item] (set-selected-items! (remove #(= item %) selected-items)))
          add-item! (fn [item] (set-selected-items! (conj selected-items item)))
-         on-chosen (fn [item {:keys [selected]}]
-                     (if (true? selected)
+         on-chosen (fn [item {:keys [selected?]}]
+                     (if (true? selected?)
                        (rm-item! item) (add-item! item)))
          [open? set-open!] (rum/use-state false)]
 
@@ -33,6 +32,8 @@
          (ui/card-title "Basic")
          (ui/card-description "x multiselect for shui"))
        (ui/card-content
+
+         ;; Basic
          (ui/dropdown-menu
            {:open open?}
            ;; trigger
@@ -45,25 +46,71 @@
            ;; content
            (x-select items selected-items
              {:on-chosen on-chosen
-              :value-render (fn [v {:keys [selected]}]
-                              (if selected
+              :value-render (fn [v {:keys [selected?]}]
+                              (if selected?
                                 [:b.text-red-800 v]
                                 [:b.text-green-800 v]))
+              :content-props
+              {:onInteractOutside #(set-open! false)
+               :class "w-48"}})
+           ))))
+
+   [:hr]
+
+   (let [items [{:key 1 :value "Apple" :class "bg-gray-800 text-gray-50"}
+                {:key 2 :value "Orange" :class "bg-orange-700 text-gray-50"}
+                nil
+                {:key 3 :value "Pear"}]
+
+         [selected-items set-selected-items!]
+         (rum/use-state [(last items)])
+
+         rm-item! (fn [item] (set-selected-items! (remove #(= item %) selected-items)))
+         add-item! (fn [item] (set-selected-items! (conj selected-items item)))
+         on-chosen (fn [item {:keys [selected?]}]
+                     (if (true? selected?)
+                       (rm-item! item) (add-item! item)))
+         [open? set-open!] (rum/use-state false)]
 
-              ;; test item render
-              ;:item-render (fn [item {:keys [selected]}]
-              ;                (if item
-              ;                  (ui/dropdown-menu-checkbox-item
-              ;                    {:checked selected
-              ;                     :on-click (fn []
-              ;                                 (if selected
-              ;                                   (rm-item! item)
-              ;                                   (add-item! item)))}
-              ;                    (:value item))
-              ;                  (ui/dropdown-menu-separator)))
-
-              :content-props {:onInteractOutside #(set-open! false)}})
-           ))))])
+     (ui/card
+       (ui/card-header
+         (ui/card-title "Search")
+         (ui/card-description "x multiselect for shui"))
+       (ui/card-content
+
+         ;; Basic
+         (ui/dropdown-menu
+           {:open open?}
+           ;; trigger
+           (ui/dropdown-menu-trigger
+             [:p.border.p-2.rounded.w-full.cursor-pointer
+              {:on-click #(set-open! true)}
+              (for [{:keys [key value class]} selected-items]
+                (ui/badge {:variant :secondary :class class} (str "#" key " " value)))
+              (ui/button {:variant :link :size :sm} "+")])
+           ;; content
+           (x-select items selected-items
+             {;; test item render
+              :search-enabled? true
+              :item-render (fn [item {:keys [selected?]}]
+                             (if item
+                               (ui/dropdown-menu-checkbox-item
+                                 {:checked selected?
+                                  :on-click (fn []
+                                              (if selected?
+                                                (rm-item! item)
+                                                (add-item! item)))}
+                                 (:value item))
+                               (ui/dropdown-menu-separator)))
+
+              ;:head-render (fn [] [:b "header"])
+              ;:foot-render (fn [] [:b "footer"])
+              :content-props
+              {:onInteractOutside #(set-open! false)
+               :class "w-48"}})
+           ))))
+
+   ])
 
 (rum/defc icon-picker-demo
   []

+ 64 - 29
deps/shui/src/logseq/shui/select/multi.cljs

@@ -9,37 +9,72 @@
       (remove nil?)
       (first))))
 
+(rum/defc search-input
+  []
+  (let [*el (rum/use-ref nil)
+        [down set-down!] (rum/use-state 0)]
+
+    (rum/use-effect!
+      (fn []
+        (when (> down 0)
+          (some-> (rum/deref *el)
+            (.closest ".head")
+            (.-nextSibling)
+            (.focus))))
+      [down])
+
+    [:div.search-input.p-2
+     {:ref *el}
+     (form/input {:placeholder "search"
+                  :class "!h-8"
+                  :on-key-down #(when (= "ArrowDown" (.-key %))
+                                  (set-down! (inc down)))
+                  :auto-focus true})]))
+
 (rum/defc x-select
   [items selected-items & {:keys [on-chosen item-render value-render
+                                  head-render foot-render
+                                  search-enabled? search-key
                                   item-props content-props]}]
   (let [x-content popup/dropdown-menu-content
         x-item popup/dropdown-menu-item]
-    (x-content
-      (merge {} content-props)
-      (for [item items
-            :let [selected? (some #(let [k (get-k item)
-                                         k' (get-k %)]
-                                     (or (= item %)
-                                       (and (not (nil? k))
-                                         (not (nil? k'))
-                                         (= k k'))))
-                              selected-items)]]
-        (if (fn? item-render)
-          (item-render item {:x-item x-item :selected selected?})
-          (let [{:keys [title value]} item
-                k (get-k item)
-                v (or title value)]
-            (when k
-              (let [opts {:selected selected?}
-                    on-click' (:on-click item-props)
-                    on-click (fn [e]
-                               ;; TODO: return value
-                               (when (fn? on-click') (on-click' e))
-                               (when (fn? on-chosen)
-                                 (on-chosen item opts)))]
-                (x-item (merge {:data-k k :on-click on-click} item-props)
-                  [:span.flex.items-center.gap-2.w-full
-                   (form/checkbox {:checked selected?})
-                   (let [v' (if (fn? v) (v item opts) v)]
-                     (if (fn? value-render)
-                       (value-render v' (assoc opts :item item)) v'))])))))))))
+    [:div.flex.flex-1.flex-col
+     (x-content
+       (merge {} content-props)
+       ;; header
+       (when (or search-enabled? (fn? head-render))
+         [:div.head
+          (when search-enabled? (search-input))
+          (when head-render (head-render))])
+       ;; items
+       (for [item items
+             :let [selected? (some #(let [k (get-k item)
+                                          k' (get-k %)]
+                                      (or (= item %)
+                                        (and (not (nil? k))
+                                          (not (nil? k'))
+                                          (= k k'))))
+                               selected-items)]]
+         (if (fn? item-render)
+           (item-render item {:x-item x-item :selected? selected?})
+           (let [{:keys [title value]} item
+                 k (get-k item)
+                 v (or title value)]
+             (when k
+               (let [opts {:selected? selected?}
+                     on-click' (:on-click item-props)
+                     on-click (fn [e]
+                                ;; TODO: return value
+                                (when (fn? on-click') (on-click' e))
+                                (when (fn? on-chosen)
+                                  (on-chosen item opts)))]
+                 (x-item (merge {:data-k k :on-click on-click} item-props)
+                   [:span.flex.items-center.gap-2.w-full
+                    (form/checkbox {:checked selected?})
+                    (let [v' (if (fn? v) (v item opts) v)]
+                      (if (fn? value-render)
+                        (value-render v' (assoc opts :item item)) v'))]))))))
+       ;; footer
+       (when (fn? foot-render)
+         [:div.foot
+          (foot-render)]))]))